.d.ts - Semantik av att sätta prototyp på deklarerade variabler i TypeScript-definitionfiler


Sammanfattning

Jag har arbetat med att uppdatera vissa typdeklarationer i DefinitelyTyped (WebXR, men det är egentligen ingen nödvändig detalj) och jag är förvirrad över den semantiska skillnaden mellan vissa sätt att definiera klasser, särskilt när dessa klasser inte kan instansieras direkt och bara kan instansieras från runtime, returnerade av DOM API-anrop.

Vad är det bästa sättet att skapa typdefinitioner för befintliga klasser i Web API, med tanke på att vissa kan instansieras direkt medan andra inte kan? Vad är syftet med att definiera prototype-fältet i ett declare var-block?

Fråga 1: declare var vs declare class

Specifikt, givet följande gränssnitt:

interface ClassInstantiable {
    something: any;
}

interface ClassNotInstantiable {
    something: any;
}

Jag förstår inte skillnaden mellan att använda declare var:

declare var ClassInstantiable: {
    new(): ClassInstantiable;
    prototype: ClassInstantiable;
};

declare var ClassNotInstantiable: {
    prototype: ClassNotInstantiable;
}

... och deklarera klass:

declare class ClassInstantiable implements ClassInstantiable { }

declare abstract class ClassNotInstantiable implements ClassNotInstantiable { }

Det är ganska tydligt att interface är nödvändigt, annars kan någon i ett efterföljande projekt vilja utöka definitionen inom sitt eget projekt.

Faktiskt förstår jag en särskild skillnad mellan de två stilarna. Med declare var-syntaxen verkar allt som definierar en konstruktor vara helt ekvivalent i typkontrolloperationer med declare class. Men att inte definiera en konstruktor i declare var gör att den inte kan instansieras, och det gör det också omöjligt att använda instanceof för att typkontrollera instanser av klassen, medan declare abstract class verkar ha exakt de funktioner jag vill ha.

Fråga 2: syftet med prototype-fältet i declare var

Dessutom provade jag ett antal varianter av olika deklarationer för att se hur typkontrollanten skulle reagera, vilket har fått mig att inse att jag inte förstår syftet med prototype-fältet i declare var-alternativet.

new-barinstanceof-bar
deklarera variabel med konstruktor och prototyp✔️✔️
deklarera var m/ konstruktor✔️✔️
deklarera var m/ prototyp❌❌
deklarera variabel (ingen konstruktor eller prototyp)❌❌
deklarera abstrakt klass❌✔️
deklarera klass✔️✔️

I lib.d.ts finns det inga instanser av declare class eller declare abstract class av något slag.

Det finns ett märkligt exempel i Audio vs HTMLAudioElement. Audio definieras utan något gränssnitt, bara en declare var med konstruktor och prototype. Audio kan instansieras och värden som returneras av det (HTMLAudioElement-värden) kan typkontrolleras med instanceof mot både Audio och HTMLAudioElement.

Men HTMLAudioElement är implementerad med ett gränssnitt och deklarera var med konstruktor. När typkontrollen körs, protesterar den inte mot att försöka anropa new HTMLAudioElement();. Jag menar, det borde den inte göra, typdefinitionen säger att det borde vara okej. Men vid körning är det ett fel, eftersom HTMLAudioElement är en ogiltig konstruktor. Så jag tycker faktiskt att detta är ett misstag.

Oavsett lyckades jag inte lista ut vad syftet med prototype-fältet var i de declare var-alternativ jag testade.