缩小范围后结果的 TypeScript 类型推断

问题描述 投票:0回答:1

我试图让 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

typescript overloading typescript-generics
1个回答
0
投票

很难理解您到底想要实现什么目标,但是,我会尽力回答提出的问题:

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);
© www.soinside.com 2019 - 2024. All rights reserved.