typescript - Cum funcționează interfețele cu semnături de construcție?
Am câteva dificultăți în a înțelege cum funcționează definirea constructorilor în interfețe. S-ar putea să înțeleg complet greșit ceva. Dar am căutat răspunsuri destul de mult timp și nu am putut găsi nimic legat de acest lucru.
Cum implementez următoarea interfață într-o clasă TypeScript:
interface MyInterface {
new ( ... ) : MyInterface;
}
Anders Hejlsberg creează o interfață care conține ceva similar cu asta în acest videoclip (în jurul minutului 14). Dar pentru viața mea nu pot să implementez asta într-o clasă.
Probabil că înțeleg greșit ceva, ce nu înțeleg?
EDITEAZĂ :
Ca să clarific. Prin „nou (... )” am vrut să spun „orice”. Problema mea este că nu reușesc să fac să funcționeze nici măcar cea mai simplă versiune a acestuia:
interface MyInterface {
new () : MyInterface;
}
class test implements MyInterface {
constructor () { }
}
Acesta nu se compilează pentru mine; primesc „Clasa 'test' declară interfața 'MyInterface' dar nu o implementează: Tipul 'MyInterface' necesită o semnătură de constructor, dar tipul 'test' nu are una” atunci când încerc să o compilez.
EDITEAZĂ :
Așa că, după ce am cercetat puțin mai mult acest lucru, având în vedere feedback-ul.
interface MyInterface {
new () : MyInterface;
}
class test implements MyInterface {
constructor () => test { return this; }
}
Nu este TypeScript valid și aceasta nu rezolvă problema. Nu poți defini tipul de returnare al constructorului. Va returna „test”. Semnătura următoare: class test { constructor() { } } Pare să fie „new() => test” (obținută prin plasarea cursorului peste „class” în editorul online cu doar acest cod lipit). Și acesta este ceea ce ne-am dori și ceea ce credeam că va fi.
Poate cineva să ofere un exemplu de acest lucru sau ceva similar unde se compilează efectiv?
EDITARE (din nou...):
Așadar, s-ar putea să fi venit cu o idee despre motivul pentru care este posibil să definești asta într-o interfață, dar nu este posibil să implementezi într-o clasă TypeScript. Următorul funcționează:
var MyClass = (function () {
function MyClass() { }
return MyClass;
})();
interface MyInterface {
new () : MyInterface;
}
var testFunction = (foo: MyInterface) : void => { }
var bar = new MyClass();
testFunction(bar);
Deci, este aceasta doar o caracteristică a TypeScript care îți permite să faci interfață cu JavaScript? Sau este posibil să o implementezi în TypeScript fără a fi nevoie să implementezi clasa folosind JavaScript?
Răspuns #1
Semnăturile de tip construct în interfețe nu pot fi implementate în clase; ele sunt doar pentru definirea API-urilor JS existente care definesc o funcție ce poate fi utilizată cu 'new'. Iată un exemplu care implică semnături new în interfețe care funcționează:
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);
Acest lucru creează o constrângere reală pentru ceea ce poți invoca cu makeObj:
class Other implements ComesFromString {
constructor (public name: string, count: number) {
}
}
makeObj(Other); // Error! Other's constructor doesn't match StringConstructable
Răspunsul #2
În căutarea mea pentru exact aceeași întrebare, am căutat cum a făcut echipa TypeScript asta...
Ei declară o interfață și ulterior o variabilă cu un nume care corespunde exact numelui interfeței. Aceasta este, de asemenea, modul de a tipiza funcțiile statice.
Exemplu din lib.d.ts:
interface Object {
toString(): string;
toLocaleString(): string;
// ... rest ...
}
declare var Object: {
new (value?: any): Object;
(): any;
(value: any): any;
// ... rest ...
}
Am încercat asta și funcționează de minune.
Răspunsul #3
Ei bine, o interfață cu o semnătură de constructor nu este menită să fie implementată de nicio clasă (la prima vedere aceasta poate părea ciudat pentru cei cu experiență în C#/Java ca mine, dar acordă-i o șansă). Este puțin diferită.
Pentru un moment, gândește-te la asta ca la o interfață cu o semnătură de apel (ca un @FunctionalInterface în lumea Java). Scopul său este de a descrie un tip de funcție... într-un fel. Semnătura descrisă ar trebui să fie satisfăcută de un obiect funcție... dar nu orice funcție de nivel înalt sau metodă. Ar trebui să fie o funcție care știe cum să construiască un obiect, o funcție care este apelată atunci când este utilizat cuvântul cheie new.
Deci, o interfață cu o semnătură de constructor definește semnătura unui constructor! Constructorul clasei tale ar trebui să respecte semnătura definită în interfață (gândește-te la asta ca și cum constructorul implementează interfața). Este ca o fabrică!
Iată un scurt fragment de cod care încearcă să demonstreze cea mai comună utilizare:
interface ClassicInterface { // old school interface like in C#/Java
method1();
...
methodN();
}
interface Factory { //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 Factory
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 Factory
constructor(n: number, s: string) { } // obviously returns an instance of ClassicInterface
method1() {}
...
methodN() {}
}
// And here is the polymorphism of construction
function instantiateClassicInterface(ctor: Factory, 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");
Răspunsul #4
Din documentația oficială
Acest lucru se datorează faptului că, atunci când o clasă implementează o interfață, este verificată doar partea de instanță a clasei. Deoarece constructorul se află în partea statică, acesta nu este inclus în această verificare.
În schimb, ar trebui să lucrați direct cu partea statică a clasei. În acest exemplu, definim două interfețe, ClockConstructor pentru constructor și ClockInterface pentru metodele de instanță. Apoi, pentru comoditate, definim o funcție constructor createClock care creează instanțe ale tipului care îi este transmis:
interface ClockConstructor {
new (hour: number, minute: number): ClockInterface;
}
interface ClockInterface {
tick(): void;
}
function createClock(
ctor: ClockConstructor,
hour: number,
minute: number
): ClockInterface {
return new ctor(hour, minute);
}
class DigitalClock implements ClockInterface {
constructor(h: number, m: number) {}
tick() {
console.log("beep beep");
}
}
class AnalogClock implements ClockInterface {
constructor(h: number, m: number) {}
tick() {
console.log("tick tock");
}
}
let digital = createClock(DigitalClock, 12, 17);
let analog = createClock(AnalogClock, 7, 32);
