Koncepčně chcete říct něco jako class MyClass<T extends object> extends T
nebo class MyClass<T extends object> implements T
, ale Strojopis nedovolí class
stupni, nebo interface
mít dynamické klíče. Všechny klíče z class
nebo interface
prohlášení musí být staticky známy. Takže pokud chcete toto chování budete muset udělat něco jiného, než class
prohlášení.
Místo toho, můžete popsat typ požadované MyClass<T>
instance, stejně jako typ MyClass
konstruktoru třídy, a pak použít typ tvrzení říci kompilátoru, že vaše skutečné konstruktoru je objekt daného typu.
Představte si, že vaše zamýšlené MyClass<T>
instance třídy mají všechny vlastnosti T
plus metoda jménem someMethodIGuess()
. Pak se vaše MyClass<T>
typ může být definován takto:
type MyClass<T extends object> = T & {
someMethodIGuess(): void;
}
Chcete, aby vaše MyClass
konstruktoru třídy mají vytvořit podpis , který bere jako argument typ T
pro některé obecný T
typ objektu, a vytváří instance MyClass<T>
. To může být definován takto:
type MyClassConstructor = {
new <T extends object>(arg: T): MyClass<T>
}
K provedení této třídy můžeme použít [Object.assign()
] zkopírovat argument konstruktoru vlastnosti do instance, plus nějaké metody nebo jiné věci, které potřebujeme.
const MyClass = class MyClass {
constructor(arg: any) {
Object.assign(this, arg);
}
someMethodIGuess() {
}
}
Ale samozřejmě pokud bychom to takhle nechat, kompilátor nebude vidět MyClass
jako MyClassConstructor
(koneckonců, to nemůže mít dynamické klíče, a ani jsem se snaží říct, to o T
zde). Potřebujeme typ tvrzení, říct to tak, jako tento:
const MyClass = class MyClass {
constructor(arg: any) {
Object.assign(this, arg);
}
someMethodIGuess() {
}
} as MyClassConstructor;
Že se zkompiluje bez chyb. Znovu, všimněte si, že kompilátor je schopen pochopit, že MyClass
provedení odpovídá MyClassConstructor
typ. Pomocí typu tvrzení, jsem přesunula břemeno zajištění, že je správně implementován od kompilátoru (což nemůžu), aby mi (nebo vás, pokud budete používat tento kód). Takže bychom měli být velmi opatrní, aby zkontrolovat, co jsme udělali a že jsme to nenapsal špatně (např. Object.assign(arg, this);
místo Object.assign(this, arg);
by být problém).
Pojďme to vyzkoušet:
const myInstance = new MyClass(({
someField: 'foo'
}))
console.log(myInstance.someField.toUpperCase()); // FOO
myInstance.someMethodIGuess(); // okay
Vypadá dobře! Kompilátor očekává myInstance
mít someField
vlastnost typ string
, stejně jako, že someMethodIGuess()
metoda.
Takže, to je to. Všimněte si, že tam jsou výhrady kolem pomocí MyClass
stejným způsobem byste použít další konstruktoru třídy. Například kompilátor bude nikdy být šťastný s class
prohlášení, že má dynamické klíče, takže pokud se budete snažit, aby generické podtřídy MyClass
s class
prohlášení, dostanete chybu:
class SubClass<T extends object> extends MyClass<T> { // error
anotherMethod() { }
}
Pokud potřebujete něco takového zjistíte sami museli použít stejný trik jako předtím s podtřídy:
type SubClass<T extends object> = MyClass<T> & { anotherMethod(): void };
type SubClassConstructor = { new <T extends object>(arg: T): SubClass<T> };
const SubClass = class SubClass extends MyClass<any> {
anotherMethod() { }
} as SubClassConstructor;
Hřiště odkaz na kód