我试图让 TypeScript 根据调用函数的上下文正确推断函数的返回类型(结果)。
TS 正确地将结果推断为
'isA' | 'isB'
,因为输入是参数。
不过,我希望 TS 在我用条件缩小可能性后,能缩小结果的类型。
天真的做法:
function matchType1<A, B>(
type: "a" | "b",
fns: { onA: () => A; onB: () => B }
) {
return type === "a" ? fns.onA() : fns.onB()
}
对象包装方法:
function matchType2<A, B>(
type: "a" | "b",
fns: { onA: () => A; onB: () => B}
) {
return type === "a"
? { a: fns.onA() }
: { b: fns.onB() }
}
重载函数的做法:
function matchType3<A, B>(type: "a", fns: { onA: () => A; onB: () => B }): A;
function matchType3<A, B>(type: "b", fns: { onA: () => A; onB: () => B }): B;
function matchType3<A, B>(
type: "a" | "b",
fns: { onA: () => A; onB: () => B }
) {
return type === "a" ? fns.onA() : fns.onB();
}
用途:
async function main(type: "a" | "b") {
const promise1 = matchType1(type, {
onA: () => Promise.resolve("isA" as const),
onB: () => Promise.resolve("isB" as const),
});
const promise2 = matchType2(type, {
onA: () => Promise.resolve("isA" as const),
onB: () => Promise.resolve("isB" as const),
});
const promise3 = matchType3(type, { // error (TS 2769) passing type as there is no overload for 'a' | 'b'
onA: () => Promise.resolve("isA" as const),
onB: () => Promise.resolve("isB" as const),
});
const [result1, result2, result3] = await Promise.all([promise1, promise2, promise3]);
if (type === "a") {
const data1 = result1; // Inferred as "isA" | "isB"
const data2 = result2[type]; // inferred as Promise<"isA"> | undefined
const data3 = result3; // correctly inferred as 'isA' but there is an error on the call to matchType3
}
注意事项
部分问题是,在我调用
type
后,会发生条件缩小 matchType*
的情况。将调用放在条件 if (type === 'a') {
内可以按预期工作。但这并不能满足我的需求。
TS 错误:
对于
matchType3
,函数重载的错误是
No overload matches this call.
Overload 1 of 2, '(type: "a", fns: { onA: () => Promise<"isA">; onB: () => Promise<"isB">; }): Promise<"isA">', gave the following error.
Argument of type '"a" | "b"' is not assignable to parameter of type '"a"'.
Type '"b"' is not assignable to type '"a"'.
Overload 2 of 2, '(type: "b", fns: { onA: () => Promise<"isA">; onB: () => Promise<"isB">; }): Promise<"isB">', gave the following error.
Argument of type '"a" | "b"' is not assignable to parameter of type '"b"'.
Type '"a"' is not assignable to type '"b"'.(2769)
解决方法
const data2 = result2[type]
并不可怕,因为它已经知道它是Promise<"isA">
或undefined
,所以我可以排除undefined
const data2 = result2[type]!
我当然不想。现在这是一个挑战。
我也尝试过条件返回类型,但结果相同。
游乐场
以上所有代码可以在TS Playground中查看; https://tsplay.dev/ND0yVN
很难理解您到底想要实现什么目标,但是,我会尽力回答提出的问题:
const promise3 = matchType3(type, {
onA: () => Promise.resolve("isA" as const),
onB: () => Promise.resolve("isB" as const),
});
TypeScript 将类型参数推断为:(参数)类型:“a” | “b”。
在 TypeScript 函数重载方法中,函数具有实现签名,但无法直接调用该签名。本质上,当您调用函数时,您实际上是在调用重载签名之一。因此,如果您传递的联合类型与任何重载签名都不匹配,TypeScript 将抛出错误。
在 matchType2 的情况下,您可以按照 https://tsplay.dev/ND0yVN 中的示例来缩小可能性,您可以使用 TypeScript 的内置实用程序,在本例中为 NonNullable 实用程序。
type Type = NonNullable<typeof result2[typeof type]>; // type Type = Promise<"isA">
const data2: "isA" = await (result2[type] as Type).then((data) => data);