typescript - 在 TypeScript 中实现 mixins 而不使用任何类型
问题描述
我正在与这个创建 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
是不可构造的。因此,解决方案是使用已知具有构造签名的类型。在 TypeScript 中,这样的签名通过将关键字添加new
到函数签名来表示:
type NewableFunctionSyntax = new () => object;
type NewableMethodSyntax = { new(): object };
这些类型都表示一个不接受任何参数的构造函数,并生成一个 assignable 类型的实例object
。请注意,虽然这些语法不同,但它们本质上是相同的。(要看到这一点,请注意编译器允许您声明 avar
多次,但如果您使用不同的类型对其进行注释,则会报错。以下编译没有错误的事实,
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 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()
会阻止你调用它会自动为您服务。(有关详细信息,请参阅microsoft/TypeScript#2310)class
.ts
推荐阅读
- azure - Kudu 中的 Powershell 进程超时
- python - 我们如何将数字附加到重复的名称,并将它们与熊猫一起上传到主 excel 文件中?
- google-colaboratory - 通过 Google Colab 使用 imread 打开图像时出现问题
- regex - 使用 Angular 表单验证未正确验证特定的正则表达式
- c# - 订购 excel 样式的行/列字符串
- ios - 使用 XCtest 在 iOS 设备上查找光标的坐标
- c# - 如何迭代其索引具有两个值并且两个值都必须插入到sql表中的列表
- angular - rxjs如何期望一个可观察的抛出错误
- javascript - 如何检查字符串是否为空,包括硬返回?
- sql - 将 Row_Number() 函数从 SQL Server 转换为 sqlite