在TypeScript中使用子类工厂模式时,超类应为哪种类型?

时间:2018-10-24 18:54:28

标签: typescript subclass factory

我正在尝试在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:⨯无法编译TypeScript:

     

src / test / subclass-factory.ts(11,37):错误TS7006:参数“ superclass”隐式具有“ any”类型。

如何更改上面的代码以成功编译并返回class的子类T,它也声明它实现了INameable

2 个答案:

答案 0 :(得分:2)

我认为您的选择是告诉Typescript superclassnew可行的,其语法是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的返回值推断可用但相当不透明的类型:

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来提取构造函数参数并从中返回类型:< / p>

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作为函数调用签名,这是调用者看到的。实现签名比较宽松,因为编译器很难验证是否可以将值分配给条件类型...因此,一个签名重载是一种为函数调用者提供更多类型安全性的方法,而不必使用实施中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 

希望这些帮助之一。祝你好运!

答案 1 :(得分:0)

这样吗?

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