class Foo {
name: string;
constructor({name}: {name: string}) {
this.name = name;
}
}
class Bar<T extends Foo> {
foo: T;
constructor({foo}: {foo: T}) {
this.foo = foo;
}
}
class CustomFoo extends Foo {
xxx: string;
constructor({
name,
xxx,
}: {
name: string,
xxx: string,
}) {
super({name});
this.xxx = xxx;
}
}
class CustomBar<F extends Foo> extends Bar<F> {
customField: string;
constructor({
foo,
customField,
}: {
foo: F,
customField: string,
}) {
super({foo});
this.customField = customField;
}
}
const doSomething = <
B extends Bar<F>,
F extends Foo,
>(
FooConstructor: { new(...args : any[]): F; },
BarConstructor: { new(...args : any[]): B; },
): B => {
return new BarConstructor({});
}
const mything = doSomething(CustomFoo, CustomBar);
不幸的是,
mything
的类型是Bar<CustomFoo>
,而我要求它是CustomBar<CustomFoo>
。
我希望 TypeScript 能够推断出返回类型。
我怎样才能以这样的方式编写
doSomething
,以便它返回推断的类型而不是“基本”类型?
我不知道该尝试什么。
不幸的是,TypeScript 的推理不会给你你想要的东西,也没有办法在 TypeScript 中以无缝或通用的方式表达你正在做的事情。 TypeScript 没有 microsoft/TypeScript#40179 中要求的“调用类型”。你会喜欢能够说:给定一些泛型类型的
BarConstructor
,当你调用构造函数时你会得到什么类型,记住它可能是泛型并且取决于它的输入或它的方式叫做。但除非你真的直接调用构造函数,否则无法捕获它。您可以使用条件类型,例如InstanceType
,但这最终会丢失任何泛型。一般限制是围绕更高种类的类型,如 microsoft/TypeScript#1213 中所要求的。没有很好的方法来处理泛型的泛型。
当您调用 doSomething(CustomFoo, CustomBar)
时,
F
被推断为
CustomFoo
,然后
B
必须被推断为
Bar<CustomFoo>
的子类型。但 TypeScript 在这里失去了线索;它不能“插入”任何地方,因为这需要更高级的类型。你能得到的最好结果是
CustomFoo
,但实际发生的情况是它又回到了
CustomBar<Foo>
的约束。 TypeScript 的推理在这里只会给你
Bar<CustomFoo>
或
CustomBar<Foo>
。相反,您需要解决它。在给定的示例中,您可以使用的一种解决方法是在
Bar<CustomFoo>
实例化表达式,以告诉编译器您特别关心将 CustomBar
插入该构造函数的类型参数中:
CustomFoo
现在,当编译器推断 const mything = doSomething(CustomFoo, CustomBar<CustomFoo>);
// const mything: CustomBar<CustomFoo>
时,唯一的选择是 B
,然后一切正常。Playground 代码链接