在不使用任何类型的情况下在 TypeScript 中实现混合
Implement mixins in TypeScript without using any type
我正在纠结这个创建 mixin 的 TypeScript 代码:
function applyMixins(derivedCtor: Function, constructors: Function[]) {
//Copies methods
constructors.forEach((baseCtor) => {
Object.getOwnPropertyNames(baseCtor.prototype).forEach((name) => {
Object.defineProperty(
derivedCtor.prototype,
name,
Object.getOwnPropertyDescriptor(baseCtor.prototype, name) ||
Object.create(null)
);
});
});
//Copies properties
constructors.forEach((baseCtor) => {
let empty = new baseCtor();
Object.keys(empty).forEach((name) => {
Object.defineProperty(
derivedCtor.prototype,
name,
Object.getOwnPropertyDescriptor(empty, name) ||
Object.create(null)
);
});
});
}
它不编译,抱怨这一行:let empty = new baseCtor();
:TS2351: This expression is not constructable. Type 'Function' has no construct signatures.
。我知道这可以通过将第一行中的 Function
类型引用与 any
交换来解决,但我试图在没有 any
的情况下过我的生活,因为它通常是草率的告诉 TypeScript 闭嘴的方法。
有没有不使用 any
来实现此代码的方法?
问题是 Function
是一个非常广泛的类型,包括任何函数,包括无法通过 new
调用的函数。因此编译器抱怨 Function
不可构造。因此,解决方案是使用 是 已知具有 construct signature 的类型。在 TypeScript 中,这样的签名是通过在函数签名前加上关键字 new
来表示的:
type NewableFunctionSyntax = new () => object;
type NewableMethodSyntax = { new(): object };
这些类型都表示一个构造函数,它不接受任何参数并生成一个可赋值类型的实例 object
。请注意,虽然这些语法不同,但它们本质上是相同的。 (要看到这一点,请注意编译器允许你多次声明一个 var
但如果你用不同的类型注释它会报错。事实上以下编译没有错误,
var someCtor: NewableFunctionSyntax;
var someCtor: NewableMethodSyntax; // no error
表明编译器将 NewableFunctionSyntax
和 NewableMethodSyntax
视为本质上可以互换。)
通过将 Function
更改为其中之一,您的代码现在可以正确编译:
function applyMixins(derivedCtor: { new(): object }, constructors: { new(): object }[]) {
//Copies methods
constructors.forEach((baseCtor) => {
Object.getOwnPropertyNames(baseCtor.prototype).forEach((name) => {
Object.defineProperty(
derivedCtor.prototype,
name,
Object.getOwnPropertyDescriptor(baseCtor.prototype, name) ||
Object.create(null)
);
});
});
//Copies properties
constructors.forEach((baseCtor) => {
let empty = new baseCtor();
Object.keys(empty).forEach((name) => {
Object.defineProperty(
derivedCtor.prototype,
name,
Object.getOwnPropertyDescriptor(empty, name) ||
Object.create(null)
);
});
});
}
让我们测试调用 applyMixins()
以确保我们了解 {new(): object}
匹配和不匹配的内容:
class Works {
x = 1;
constructor() { }
}
applyMixins(Works, []); // okay
Works
很好,因为它是一个不带参数的 class 构造函数。
class CtorRequiresArg {
y: string;
constructor(y: string) { this.y = y; }
}
applyMixins(CtorRequiresArg, []); // error!
// -------> ~~~~~~~~~~~~~~~
// Type 'new (y: string) => CtorRequiresArg'
// is not assignable to type 'new () => object'
CtorRequiresArg
失败,因为在构造它时必须传递一个 string
参数,例如 new CtorRequiresArg("hello")
... 但是 applyMixins()
只接受可以调用的构造函数没有任何争论。
最后:
function NotACtor() { }
applyMixins(NotACtor, []); // error!
// -------> ~~~~~~~~
// Type '() => void' provides no match
// for the signature 'new (): object'
NotACtor
失败,因为它不被认为是可构造的。这可能令人惊讶,因为在运行时没有什么可以阻止你调用 new NotACtor()
,但编译器认为如果你想要一个 class 构造函数,你将在 [=35] 中使用 class
表示法=] 文件...即使是针对 ES5 运行时,因为 TypeScript 会自动为您降级。 (有关详细信息,请参阅 microsoft/TypeScript#2310)
我正在纠结这个创建 mixin 的 TypeScript 代码:
function applyMixins(derivedCtor: Function, constructors: Function[]) {
//Copies methods
constructors.forEach((baseCtor) => {
Object.getOwnPropertyNames(baseCtor.prototype).forEach((name) => {
Object.defineProperty(
derivedCtor.prototype,
name,
Object.getOwnPropertyDescriptor(baseCtor.prototype, name) ||
Object.create(null)
);
});
});
//Copies properties
constructors.forEach((baseCtor) => {
let empty = new baseCtor();
Object.keys(empty).forEach((name) => {
Object.defineProperty(
derivedCtor.prototype,
name,
Object.getOwnPropertyDescriptor(empty, name) ||
Object.create(null)
);
});
});
}
它不编译,抱怨这一行:let empty = new baseCtor();
:TS2351: This expression is not constructable. Type 'Function' has no construct signatures.
。我知道这可以通过将第一行中的 Function
类型引用与 any
交换来解决,但我试图在没有 any
的情况下过我的生活,因为它通常是草率的告诉 TypeScript 闭嘴的方法。
有没有不使用 any
来实现此代码的方法?
问题是 Function
是一个非常广泛的类型,包括任何函数,包括无法通过 new
调用的函数。因此编译器抱怨 Function
不可构造。因此,解决方案是使用 是 已知具有 construct signature 的类型。在 TypeScript 中,这样的签名是通过在函数签名前加上关键字 new
来表示的:
type NewableFunctionSyntax = new () => object;
type NewableMethodSyntax = { new(): object };
这些类型都表示一个构造函数,它不接受任何参数并生成一个可赋值类型的实例 object
。请注意,虽然这些语法不同,但它们本质上是相同的。 (要看到这一点,请注意编译器允许你多次声明一个 var
但如果你用不同的类型注释它会报错。事实上以下编译没有错误,
var someCtor: NewableFunctionSyntax;
var someCtor: NewableMethodSyntax; // no error
表明编译器将 NewableFunctionSyntax
和 NewableMethodSyntax
视为本质上可以互换。)
通过将 Function
更改为其中之一,您的代码现在可以正确编译:
function applyMixins(derivedCtor: { new(): object }, constructors: { new(): object }[]) {
//Copies methods
constructors.forEach((baseCtor) => {
Object.getOwnPropertyNames(baseCtor.prototype).forEach((name) => {
Object.defineProperty(
derivedCtor.prototype,
name,
Object.getOwnPropertyDescriptor(baseCtor.prototype, name) ||
Object.create(null)
);
});
});
//Copies properties
constructors.forEach((baseCtor) => {
let empty = new baseCtor();
Object.keys(empty).forEach((name) => {
Object.defineProperty(
derivedCtor.prototype,
name,
Object.getOwnPropertyDescriptor(empty, name) ||
Object.create(null)
);
});
});
}
让我们测试调用 applyMixins()
以确保我们了解 {new(): object}
匹配和不匹配的内容:
class Works {
x = 1;
constructor() { }
}
applyMixins(Works, []); // okay
Works
很好,因为它是一个不带参数的 class 构造函数。
class CtorRequiresArg {
y: string;
constructor(y: string) { this.y = y; }
}
applyMixins(CtorRequiresArg, []); // error!
// -------> ~~~~~~~~~~~~~~~
// Type 'new (y: string) => CtorRequiresArg'
// is not assignable to type 'new () => object'
CtorRequiresArg
失败,因为在构造它时必须传递一个 string
参数,例如 new CtorRequiresArg("hello")
... 但是 applyMixins()
只接受可以调用的构造函数没有任何争论。
最后:
function NotACtor() { }
applyMixins(NotACtor, []); // error!
// -------> ~~~~~~~~
// Type '() => void' provides no match
// for the signature 'new (): object'
NotACtor
失败,因为它不被认为是可构造的。这可能令人惊讶,因为在运行时没有什么可以阻止你调用 new NotACtor()
,但编译器认为如果你想要一个 class 构造函数,你将在 [=35] 中使用 class
表示法=] 文件...即使是针对 ES5 运行时,因为 TypeScript 会自动为您降级。 (有关详细信息,请参阅 microsoft/TypeScript#2310)