TS 错误地推断路口类型

TS incorrectly inferring intersection type

我们遇到了一个无法解决的 TS 错误,我想知道是否有人知道如何修复它。 TS 似乎在 combo.input 上推断出一个交集类型,这看起来很奇怪(联合类型对我来说更有意义)。

我在下面总结了一个例子。

我们最终只是使用了不同的方法,但我觉得应该有一种方法可以键入函数来修复错误。

const stringFunc = (input: string): string => input;
const numFunc = (input: number): number => input;

const stringCombo = { funct: stringFunc, input: 'hello' };
const numCombo = { funct: numFunc, input: 4 };

type Combo = typeof stringCombo | typeof numCombo;

const processCombo = (combo: Combo) => {
  return combo.funct(combo.input);  
     // Argument of type 'string | number' is not assignable to parameter of type 'never'.
};

这并没有错,更多的是设计限制。 combo.funct 是函数的并集。这意味着它可以是接受 string 的函数或接受 number 的函数,但在调用时无法知道它是哪个。因此,调用 combo.funct 的唯一安全方法是使用可以满足两个函数签名的参数。在这种情况下,该类型将是 number & string,它会减少到 never(如果这些函数接受不同的对象类型,则可能有一个值同时满足这两种对象类型)

在这种特殊情况下,函数和参数实际上是相关的,但打字稿不跟踪这一点。它所能看到的是 combo.input 的类型(即 string | number)不能作为函数联合的参数 combo.funct.

您可以使用有区别的联合:

const stringFunc = (input: string): string => input;
const numFunc = (input: number): number => input;

const stringCombo = { funct: stringFunc, input: 'hello', type: "string" as const };
const numCombo = { funct: numFunc, input: 4 , type: "number" as const};

type Combo = typeof stringCombo | typeof numCombo;

const processCombo = (combo: Combo) => {
  return combo.type === "string"?
      combo.funct(combo.input):
      combo.funct(combo.input);
};

Playground Link

或者因为在这种情况下我们显然比编译器了解更多,所以我们可以使用类型断言:

const processCombo = (combo: Combo) => {
  return combo.funct(combo.input as never);  
};

Playground Link

除了 Titian Cernicova-Dragomir 的回答之外, 您还可以将您的类型建模为泛型:

type FunTtoT<T> = (t: T) => T;

const stringFunc: FunTtoT<string> = (input: string): string => input;
const numFunc: FunTtoT<number> = (input: number): number => input;

type Combo<T extends string | number> =  { funct: FunTtoT<T>, input: T };

const stringCombo = { funct: stringFunc, input: 'hello' };
const numCombo = { funct: numFunc, input: 4 };

function processCombo<T extends string | number>(combo: Combo<T>) {
  return combo.funct(combo.input);  
};

const s = processCombo(stringCombo);  // inferred to string
const n = processCombo(numCombo);     // inferred to number

我更喜欢这种方法来区分联合和类型断言,因为输出类型被推断为单一类型,而不是联合。

Playground