【问题标题】:Implement mixins in TypeScript without using any type在 TypeScript 中实现 mixins 而不使用任何类型
【发布时间】:2021-01-10 14:28:51
【问题描述】:

我正在处理这个创建 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 () => object{new(): object} 是可构造的。 this 是否适用于您的用例?如果是这样,我会在有机会时写一个答案。如果不是,请在问题文本中用相关示例进行详细说明。祝你好运!
  • 谢谢,我在模块里加了declare type ctorType = new() => object;,然后用ctorType代替Function。但是和{new(): object}相比有什么区别呢?

标签: typescript mixins


【解决方案1】:

问题在于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

表明编译器将NewableFunctionSyntaxNewableMethodSyntax 视为本质上可以互换。)


通过将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(),但编译器认为,如果您想要一个类构造函数,您将在 .ts 文件中使用 class 表示法...即使针对ES5 运行时,因为 TypeScript 会自动为您降级。 (更多信息请参见microsoft/TypeScript#2310


Playground link to code

【讨论】:

  • 是否存在捕获任何可构造物的类型声明,而不仅仅是具有特定签名的可构造物?我尝试使用new(...args: unknown[]) => object;,但这不允许具有特定类型参数的构造函数。使用new(...args: any[]) => object;,编译器不再抱怨,但是我们使用了参考any,这违背了我的问题的目的。
猜你喜欢
  • 2019-08-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-27
  • 2021-03-15
  • 2021-12-22
  • 1970-01-01
相关资源
最近更新 更多