在 TypeScript 中使用子类工厂模式时,超类应该是什么类型?

Of what type should a superclass be when using a subclass factory pattern in TypeScript?

我正在尝试在 TypeScript 中实现子类工厂模式 3.x。 考虑这个测试:

import { expect } from 'chai'

describe('subclass factory', () => {
  it('should work', () => {
    interface INameable {
      name?: string
    }

    const name = 'I am a Nameable!'

    function nameableSubclassOf<T> (superclass) {
      return class extends superclass implements INameable {
        name?: string = name
      }
    }

    class Foo {}

    const NameableFoo = nameableSubclassOf(Foo)
    const nameableFoo = new NameableFoo()

    expect(nameableFoo).to.be.instanceOf(Foo)
    expect(nameableFoo.name).to.be.ok
    expect(nameableFoo.name).to.equal(name)
  })
})

编译失败并显示以下消息:

TSError: ⨯ Unable to compile TypeScript:

src/test/subclass-factory.ts(11,37): error TS7006: Parameter 'superclass' implicitly has an 'any' type.

如何更改以上代码以成功编译和 return 一个 classT 的子类,它还声明它实现了 INameable

这边?

function nameableSubclassOf(superclass: {new(): any}) {

我认为您的选择要么是告诉 Typescript superclassnewable,其语法是 briefly alluded to in the TypeScript handbook:

// "new (...args: any[]) => any" means the constructor takes any number of arguments 
// and returns anything

function nameableSubclassOf<C extends new (...args: any[]) => any>(superclass: C) {
  return class extends superclass implements INameable {
    name?: string = name
  }
}

这应该允许编译器为 nameableSubclassOf 的 return 值推断出可用但相当不透明的类型:

const NameableFoo = nameableSubclassOf(Foo)
// const NameableFoo: {
//   new (...args: any[]): nameableSubclassOf<typeof Foo>.(Anonymous class);
//   prototype: nameableSubclassOf<any>.(Anonymous class);
// } & typeof Foo 

const nameableFoo = new NameableFoo();
// const nameableFoo: nameableSubclassOf2<typeof Foo>.(Anonymous class) & Foo; 

const fooName = nameableFoo.name;
// const fooName: string | undefined; 

... 或者,如果你想要一个不依赖于匿名 类 的更明确的类型,你可以使用泛型来指定超类和 conditional types 来提取构造函数参数和return 从中输入:

function nameableSubclassOf<C extends new (...args: any[]) => any>(
  superclass: C
): C extends new (...args: infer A) => infer T ? new (...args: A) => T & INameable : never;
function nameableSubclassOf(
  superclass: new (...args: any[]) => any
): new (...args: any[]) => INameable {
  return class extends superclass implements INameable {
    name?: string = name
  }
}

请注意,我在实现中使用了单个 overload for the function call signature, which is what the caller sees. The implementation signature is looser because it's hard for the compiler to verify that a value is assignable to a conditional type... so the one-signature-overload is a way to get more type safety for the caller of a function without having to use loads of type assertions

这更冗长,但是当你使用它时你会得到更好的类型:

const NameableFoo = nameableSubclassOf(Foo)
// const NameableFoo: new () => Foo & INameable 

const nameableFoo = new NameableFoo()
// const nameableFoo: Foo & INameable 

const fooName = nameableFoo.name
// const fooName: string | undefined 

希望其中之一能有所帮助。祝你好运!