Hur man låter TypeScript förstå existensen av statiska medlemmar som dynamiskt läggs till en klass?
Jag har en QueryBuilder och skulle vilja presentera alla dess metoder som statiska medlemmar på Model-klassen.
Tydligen är det giltig Javascript-kod. Men hur kan jag få TypeScript att förstå existensen av dessa statiska metoder på Model?
function queryable (target: any) {
for (const prop in target.prototype.builder) {
if (prop === 'constructor' || target.prototype.hasOwnProperty(prop)) {
continue;
}
target[prop] = (...args: any[]): any => {
return target.prototype.builder[prop](...args);
};
}
}
class QueryBuilder {
where(prop: string, value: string) {
console.log(prop, value);
console.log("I've presented on model class as static method.");
}
// ...
}
@queryable
class Model {
get builder () {
return new QueryBuilder();
}
// ...
}
// calling static where
Model.where('color', 'red');
Egenskapen 'where' finns inte på typen 'typeof Model'.
Se i Typescript Playground
Svar #1
Problemet du bör hålla ett öga på är TypeScript#4881 men för närvarande (3.5.1) kan en decorator inte ändra typen på den klass den dekorerar.
Det sagt, du kan fortfarande definiera dekoratören på samma sätt men kalla den uttryckligen snarare än som en dekoratör, precis som många TypeScript-projekt som använder Reduxs populära connect-dekoratör måste göra.
class _Model {
get builder () {
return new QueryBuilder();
}
// ...
}
const Model = queryable(_Model);
eller om det är huvudexporten av en modul kan du klara dig med ett namn:
class Model {
get builder () {
return new QueryBuilder();
}
// ...
}
export default queryable(Model);
Då är tricket att bättre kommentera dekoratorn för att lära TypeScript alla tillägg queryable har gjort till dess target:
function queryable<T extends new (...args: any[]) => {builder: any}>(target: T): InstanceType<T>['builder'] & (typeof target) {
for (const prop in target.prototype.builder) {
if (prop === 'constructor' || target.prototype.hasOwnProperty(prop)) {
continue;
}
(target as any)[prop] = (...args: any[]): any => {
return target.prototype.builder[prop](...args);
};
}
return target as any
}
Länk till lekplats
Svar #2
Eftersom Typescript är ett språk med stark typning kontrollerar det alltid om typanvändning matchar typgränssnitt. Denna kontroll sker under transpilering, när Typescript inte har någon aning om att du kommer att lägga till en ny metod. Eftersom du lägger till den vid körning. Det enda sättet att kalla en sådan metod är att göra en typomvandling till något allmänt som any:
(<any>Model).where('color', 'red');
Det är som att du säger till Typescript att du är säker på att metoden finns och att transpillern inte ska oroa sig för det.
