条件类型中的TypeScript类型推断

时间:2018-05-09 22:44:03

标签: typescript types type-inference

我对以下示例中推断类型的方式感到困惑

type RetType<T> = T extends (...args: (infer I)[]) => infer R ? [I, R] : any;
type X = (a: number, b: string) => void;
type Q = RetType<X>;

如果您将鼠标悬停在操场上的Q类型上,您将获得[number & string, void]。这令人困惑,因为我希望将I推断为number | string(联合)而不是number & string(交集)。

有没有人理解为什么输入参数被推断为交集而不是联合?

2 个答案:

答案 0 :(得分:5)

TL; DR:因为无论I是什么,都必须将其分配给所有函数类型T的参数。< / p>

这是因为函数参数是 contra-variant 。这只意味着,对于一个函数来代替另一个函数,它的参数类型必须与其他函数相同或更通用。当你看一个例子时,这是非常明显的:

type f: (arg: string) => string;
type g: (arg: "foo") => string;

// f is assignable to g, since a function expecting
// to receive any string should have no problem accepting
// the specific string "foo".

// However, the reverse isn't true. You can't assign g to f,
// since g expects to receive exactly the string "foo" for its
// argument, but if it's used in place of f, it can receive any string.

换句话说,f可分配给g,因为g的参数可分配给f&#39}。这种逆转是 contra 部分。

因此,如果T是某个神秘函数类型(...args: I[]) => R的子类型,则参数contra-variance告诉我们I必须可赋值给T的参数类型。

因此,T extends (...args: (infer I)[]) => infer R告诉typescript推断某个单一类型I,以便可以使用I代替{em> T的任何参数

因此,对于类型X,无论I是什么,它都应该是可以分配给两个参数的。由于参数类型分别是numberstring,我们会问:哪些类型可分配给这两种类型?

好吧,number & string

*如需了解更多信息,您可能有兴趣阅读co and contra-variance

答案 1 :(得分:3)

这可能不是您正在寻找的答案或解释,但这在docs中被提及:

  

同样,反变量位置中相同类型变量的多个候选者会导致交叉类型被推断:

type Bar<T> = T extends { a: (x: infer U) => void, b: (x: infer U) => void } ? U : never;
type T20 = Bar<{ a: (x: string) => void, b: (x: string) => void }>;  // string
type T21 = Bar<{ a: (x: string) => void, b: (x: number) => void }>;  // string & number