T extends { new(...args: any[]): {} }
означает, что T
должна быть функцией конструктора (т.е. классом).Аргументы конструктора, а также тип возвращаемого значения не имеют значения (T
может иметь любое количество аргументов и может возвращать любой тип, который расширяет {}
, фактически любой тип объекта).
Если вызывается напрямую, T
будет классом.Подход, используемый для ввода этого декоратора, в основном состоит из миксинов (описан для машинописи здесь ).
Возвращаемое значение функции будет новым классом, которыйнаследует декорированный класс.Таким образом, это не добавление конструктора, а замена оригинального конструктора новым и вызов оригинального конструктора с помощью вызова super
.
Обобщения, на мой взгляд, немного излишни в этой ситуации.Они полезны для миксинов, потому что они перенаправляют исходный класс из входного параметра в выходной параметр (а миксин добавляет члены к типу).Но поскольку декораторы не могут изменить структуру типа, пересылать нечего.Кроме того, вместо конструктора, возвращающего {}
, я бы набрал его, чтобы он возвратил Record
, поскольку вы проверяете это во время выполнения, может также проверить это и во время компиляции:
export class Record{
protected _completeInitialization(): void {}
};
export function Model() {
return (ctr: new (...a: any[]) => Record ) => {
if (!(ctr.prototype instanceof Record)) {
throw new RecordMissingExtendsError(ctr);
}
return (class extends ctr {
constructor(...args: any[]) {
const [data] = args;
if (data instanceof ctr) {
return data;
}
super(...args);
this._completeInitialization(); // no assertion since constructor returns a record
}
});
};
}
@Model()
class MyRecord extends Record { }
@Model()// compile time error, we don't extend Record
class MyRecord2 { }