我看到TypeScript有点奇怪的情况。我试图创建一个最小的可复制示例,以便可以在TypeScript Playground中轻松对其进行测试。
BaseA
的类型,根据其类型参数之一,它可以具有不同的结构。DFunction
的类型,它表示一个接收BaseA
类型的对象的函数。DFunction
的返回类型应不同,具体取决于接收到的BaseA
的类型。type BaseA<P, Special=false> = {
payload: P;
} & Special extends true ? {_special:true} : {};
type DFunction = {
<A extends BaseA<any, false>>(a: A): A;
<A extends BaseA<any, true>>(a: A): Promise<A>;
};
function test(
d: DFunction,
normalA: BaseA<{x:number}, false>,
specialA: BaseA<{x:number}, true>
) {
// Since we are passing a `specialA` to `d`, the return type should be a Promise.
d(specialA).then(() => {});
}
由于TypeScript无法理解d(specialA)
返回了Promise,因此该代码在最后一行出错。我该如何输入DFunction
,使其返回类型取决于其输入类型?
我想指出的一件事是,该模式确实适用于更简单的示例:
type DFunction = {
<A extends string>(a: A): A;
<A extends number>(a: A): Promise<A>;
};
function test(
d: DFunction
) {
d(5).then(() => {}); // no errors
const b = d("5"); // `b` is inferred to be of type `string`
}
答案 0 :(得分:3)
问题是因为BaseA<P, true>
是BaseA<P, false>
的子类型,并且因为接受超类型的重载签名出现在接受子类型的过载签名之前。当两个不同的重载签名均适用时,Typescript将不会选择“最具体的”签名;它只会选择找到的第一个。
因此,有两种解决方案:您可以像这样更改过载签名的顺序,
type DFunction = {
<A extends BaseA<any, true>>(a: A): Promise<A>;
<A extends BaseA<any, false>>(a: A): A;
};
或者您可以更改BaseA
的定义,以使BaseA<P, true>
不能分配给BaseA<P, false>
,如下所示:
type BaseA<P, Special=false> = {
payload: P;
} & (Special extends true ? {_special: true} : {_special?: undefined});