jestjs - Typescript'te window.addEventListener türü nasıl yeniden tanımlanır?


Test amaçları için, window.addEventListener'ı taklit etmem gerekiyor (jest ile boş bir mock sadece gözetlemek için yeterlidir). Bunu şu şekilde yapıyorum:

window.addEventListener = jest.fn();
window.removeEventListener = jest.fn();

Sorun şu ki, window.addEventListener.mockClear() yaptığımda, TypeScript bu metodun var olmadığını belirtiyor.

Bununla başa çıkmanın en iyi yolu nedir?

Geçici çözüm: Şu anda bunu yapıyorum ama bunu sürekli yapmak istemiyorum

(window.addEventListener as jest.Mock<{}>).mockClear();
(window.addEventListener as jest.Mock<{}>).mockClear();

Cevap #1

Mevcut arayüzü, o arayüzde ilan edilen metodlara bazı özellikler eklemek için yükseltmenin mümkün olduğunu düşünmüyorum.

Ama bu yöntemlerin üzerine yazabilirsiniz. Tür dönüşümlerini sürekli yazmaktan kaçınmanın bir yolu vardır: ihtiyacınız olduğu şekilde türlenmiş addEventListener ve removeEventListener sahip kendi MockWindow arayüzünüzü tanımlayabilirsiniz. Sonra gerçek window nesnesini bir kez MockWindow olarak dönüştürebilir ve sonrasında bunu kullanabilirsiniz:

interface MockWindow extends Window {
    addEventListener: jest.Mock<{}> & typeof window.addEventListener;
    removeEventListener: jest.Mock<{}> & typeof window.removeEventListener;
}

function mockWindow() {
    window.addEventListener = jest.fn();
    window.removeEventListener = jest.fn();
    return window as MockWindow;
}

let w = mockWindow();


w.addEventListener('load',function() {});
w.addEventListener.mockClear();  

Aslında, mevcut jest tip tanımlarında, jest.Mock arayüzü zaten çok gevşek bir çağrı imzasına sahip olduğu için

interface Mock<T> extends Function {
    ...
    (...args: any[]): any;

jest.Mock olarak beyan edilen herhangi bir şey, herhangi sayıda herhangi parametre ile çağrılabilir ve addEventListener türüne & typeof window.addEventListener eklemeyi gereksiz kılar.

GÜNCELLEME: Görünüşe göre TypeScript yeterince ifade gücüne sahip, bu yüzden genel ve tip kontrolü yapılmış bir fonksiyon yazabilirsiniz:

function mockMethods<T, MethodName extends keyof T>(o: T, methodNames:MethodName[])
: T & {[K in MethodName]: T[K] & jest.Mock<{}>} {
    methodNames.forEach(k => {
       o[k] = jest.fn() as any;
    });
    return o as any; // typescript doesn't allow this cast: 
                     // as (T & {[K in MethodKeys]: T[K] & jest.Mock<{}>});
}

let w = mockMethods(window, ['addEventListener', 'removeEventListener']);

w.addEventListener('load', function() {});
w.addEventListener.mockClear();

// you can still call other window methods
w.alert('x');

// and their types are unaffected
w.alert.mockClear(); // error: Property 'mockClear' does not exist 
                     // on type '(message?: any) => void'.

// and you can't mock non-existent methods
let u = mockMethods(window, ['z']); // error: Argument of type '"z"[]'  
                     //  is not assignable to parameter of type '("blur" | ...