我想学习Typescript尝试使用条件类型推断签名中的类型的算法。
示例1:这里我们正确地将T
推断为number
:
type Args<T> = { value: T }
function foo<T>(args: Args<T>) { return args; }
// correctly inferred as foo<number>
foo({ value: 123 });
示例2:条件类型和推断unknown
type StringOrNever<T> = T extends string ? string : never;
type Args<T> = { value: StringOrNever<T>; }
function foo<T>(args: Args<T>) { return args; }
// both calls inferred as foo<unknown> :(
foo({ value: 123 });
foo({ value: "some string" });
示例3:正确(或"预期"(推理但类型怪异的
type StringOrNever<T> = T extends string ? string : never;
type Args<T> = { value: StringOrNever<T> & T; }
function foo<T>(args: Args<T>) { return args; }
// inferred as foo<number>
foo({ value: 123 });
// inferred as foo<"Typescript">
foo({ value: "Typescript" });
我想学习的是:
- 为什么Typescript在示例2中为
T
推断unknown
,以及是什么导致它像我在示例(3(中预期的那样为T
推断类型 - 如果Typescript遵循某种通用算法来尝试推断泛型类型参数。例如,尝试一系列候选者,其中第一个候选者总是一种类型的自变量,最后一个候选者是
unknown
(有点像最后的手段(
泛型类型推理在TypeScript中是一个复杂的概念,有时很难理解。但泛型类型必须用于可以被给定参数的类型替换的位置。
例如1
function foo<T>(args: { value: T }) { return args; }
如果传递类型为{ value: string }
的值,我们可以用string
替换T
。
实施例2的情况并非如此。
type StringOrNever<T> = T extends string ? string : never;
function foo<T>(args: { value: StringOrNever<T> }) { return args; }
T
仅用于条件的比较部分。在条件的右侧,我们只有string
和never
。T
不在任何可以被给定类型替换的位置,因此unknown
被推断为默认类型。
如果你想推理成功,你需要把T
放在条件的右边。
type StringOrNever<T> = T extends string ? T : never;
function foo<T>(args: { value: StringOrNever<T> }) { return args; }
这允许TypeScript基于给定类型的value
来推断T
。
游乐场