我正在尝试在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
?
答案 0 :(得分:2)
我认为您的选择是告诉Typescript superclass
是new
可行的,其语法是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}) {