Come funzionano le interfacce typescript con le firme dei costrutti?

Sto avendo qualche problema a capire come funziona la definizione dei costruttori nelle interfacce. Potrei essere totalmente frainteso qualcosa. Ma ho cercato le risposte per un bel po ‘e non riesco a trovare nulla relativo a questo.

Come implementare la seguente interfaccia in una class TypeScript:

interface MyInterface { new ( ... ) : MyInterface; } 

Anders Hejlsberg crea un’interfaccia contenente qualcosa di simile a questo in questo video (a circa 14 minuti). Ma per la vita di me non posso implementarlo in una class.

Probabilmente fraintenderò qualcosa, cosa non otterrò?

MODIFICARE:

Chiarire. Con “nuovo (…)” intendevo “qualsiasi cosa”. Il mio problema è che non riesco a ottenere nemmeno la versione di base di questo lavoro:

 interface MyInterface { new () : MyInterface; } class test implements MyInterface { constructor () { } } 

Non sto compilando per me ottengo “Test di class” dichiara l’interfaccia “MyInterface” ma non la implementa: Digitare “MyInterface” richiede una firma di costrutto, ma Digita “test” ne manca una “quando si tenta di compilarlo.

MODIFICARE:

Quindi, dopo aver ricercato questo un po ‘di più dato il feedback.

 interface MyInterface { new () : MyInterface; } class test implements MyInterface { constructor () => test { return this; } } 

Non è valido TypeScript e questo non risolve il problema. Non è ansible definire il tipo di reso del costruttore. Restituirà “test”. La firma di quanto segue: class test {constructor () {}} Sembra essere “new () => test” (ottenuto passando con il mouse sopra “class” nell’editor online con solo quel codice incollato). E questo è ciò che vorremmo e quello che pensavo sarebbe stato.

Qualcuno può fornire un esempio di questo o qualcosa di simile dove si sta effettivamente compilando?

EDIT (di nuovo …):

Quindi potrei avere un’idea del motivo per cui è ansible definire questo in un’interfaccia ma non è ansible implementarlo in una class TypeScript. I seguenti lavori:

 var MyClass = (function () { function MyClass() { } return MyClass; })(); interface MyInterface { new () : MyInterface; } var testFunction = (foo: MyInterface) : void => { } var bar = new MyClass(); testFunction(bar); 

Quindi questa è solo una funzionalità di TypeScript che ti permette di interfacciare javascript? Oppure è ansible implementarlo in TypeScript senza dover implementare la class usando javascript?

Costruire firme nelle interfacce non sono implementabili nelle classi; sono solo per definire le API JS esistenti che definiscono una funzione “nuova”. Ecco un esempio che riguarda le interfacce di new firme che funzionano:

 interface ComesFromString { name: string; } interface StringConstructable { new(n: string): ComesFromString; } class MadeFromString implements ComesFromString { constructor (public name: string) { console.log('ctor invoked'); } } function makeObj(n: StringConstructable) { return new n('hello!'); } console.log(makeObj(MadeFromString).name); 

Questo crea un vincolo reale per ciò che è ansible richiamare makeObj con:

 class Other implements ComesFromString { constructor (public name: string, count: number) { } } makeObj(Other); // Error! Other's constructor doesn't match StringConstructable 

Alla mia ricerca della stessa identica domanda sono andato a osservare come il Team TypeScript ha fatto questo …

Stanno dichiarando un’interfaccia e successivamente una variabile con un nome che corrisponde esattamente al nome dell’interfaccia. Questo è anche il modo di digitare le funzioni statiche.

Esempio da lib.d.ts :

 interface Object { toString(): string; toLocaleString(): string; // ... rest ... } declare var Object: { new (value?: any): Object; (): any; (value: any): any; // ... rest ... } 

L’ho provato e funziona come un incantesimo.

Dal punto di vista del design, non è usuale specificare i requisiti del costruttore in un’interfaccia. L’interfaccia dovrebbe descrivere le operazioni che è ansible eseguire su un object. Le classi differenti che implementano l’interfaccia dovrebbero poter richiedere parametri di costruzione diversi se necessario.

Ad esempio, se avessi un’interfaccia:

 interface ISimplePersistence { load(id: number) : string; save(id: number, data: string): void; } 

Potrei avere implementazioni per la memorizzazione dei dati come cookie, che non richiede parametri di costruzione e una versione che memorizza i dati in un database, che ha bisogno di una stringa di connessione come parametro del costruttore.

Se vuoi ancora definire i costruttori in un’interfaccia, c’è un modo sporco per farlo, che ho usato per rispondere a questa domanda:

Interfacce con le firme del costrutto non controllo del tipo

Beh, un’interfaccia con la firma del costrutto non è pensata per essere implementata da nessuna class (a prima vista potrebbe sembrare strano per ragazzi con C # / Java come me, ma dargli una possibilità). È leggermente diverso.

Per un momento pensalo come un’interfaccia con una firma di chiamata (come una @FunctionalInterface nel mondo Java). Il suo scopo è quello di descrivere un tipo di funzione … di. La firma descritta dovrebbe essere soddisfatta da un object funzione … ma non solo una funzione di alto livello o un metodo. Dovrebbe essere una funzione che sa come build un object, una funzione che viene chiamata quando viene utilizzata una new parola chiave.

Quindi un’interfaccia con una firma di costruzione definisce la firma di un costruttore! Il costruttore della tua class che dovrebbe rispettare la firma definita nell’interfaccia (pensaci come il costruttore implementa l’interfaccia). È come un costruttore o una fabbrica!

Ecco un breve frammento di codice che tenta di dimostrare l’utilizzo più comune:

 interface ClassicInterface { // old school interface like in C#/Java method1(); ... methodN(); } interface Builder { //knows how to construct an object // NOTE: pay attention to the return type new (myNumberParam: number, myStringParam: string): ClassicInterface } class MyImplementation implements ClassicInterface { // The constructor looks like the signature described in Builder constructor(num: number, s: string) { } // obviously returns an instance of ClassicInterface method1() {} ... methodN() {} } class MyOtherImplementation implements ClassicInterface { // The constructor looks like the signature described in Builder constructor(n: number, s: string) { } // obviously returns an instance of ClassicInterface method1() {} ... methodN() {} } // And here is the polymorphism of construction function instantiateClassicInterface(ctor: Builder, myNumberParam: number, myStringParam: string): ClassicInterface { return new ctor(myNumberParam, myStringParam); } // And this is how we do it let iWantTheFirstImpl = instantiateClassicInterface(MyImplementation, 3.14, "smile"); let iWantTheSecondImpl = instantiateClassicInterface(MyOtherImplementation, 42, "vafli"); 

Per ottenere il comportamento desiderato è ansible utilizzare Decorators , anche se probabilmente non è quello per cui dovrebbero essere usati.

Questo

 interface MyInterface { new (); } function MyInterfaceDecorator(constructor: MyInterface) { } @MyInterfaceDecorator class TestClass { constructor () { } } 

compila senza problemi. Al contrario, la seguente definizione di TestClass

 // error TS2345: Argument of type 'typeof TestClass' is not assignable to parameter of type 'MyInterface'. @MyInterfaceDecorator class TestClass { constructor (arg: string) { } } 

non verrà compilato.