从函数数组推导参数类型时结果为never的问题求助
我最近在写TypeScript的时候碰到了个头疼的问题——想从一个函数数组里提取所有函数的第一个参数类型,结果用infer推出来的类型居然是never,试了好多次都没搞明白问题出在哪。
先给大家看一下我的代码:
首先定义了这样一个函数数组类型:
type ArrayOfItem = [ (a: "type_a") => {}, (a: "type_b") => {}, ];
我想用infer把里面所有a的类型提取出来,于是写了这段类型推断代码:
type TypeOfAllData = ArrayOfItem[number] extends (a: infer T) => void ? T : never;
本以为能得到"type_a" | "type_b",结果TypeOfAllData的类型却是never,这可把我整懵了。有没有懂行的朋友能帮我分析下原因,再给个解决办法呀?
问题根源分析
其实这是TypeScript联合类型条件推断的一个特性:当你在逆变位置(比如函数的参数位置)做infer推断时,TypeScript会取各个分支推断结果的交集,而不是我们预期的并集。
这里ArrayOfItem[number]本质是一个联合类型:((a: "type_a") => {}) | ((a: "type_b") => {})。当直接对这个联合类型做extends (a: infer T) => void的推断时,TypeScript会试图找一个能同时满足两个函数参数要求的T——也就是"type_a"和"type_b"的交集,可这两个是完全不同的字面量类型,它们的交集自然就是never了。
解决办法
要拿到我们想要的联合类型,就得用分布式条件类型——让TypeScript把联合类型拆成单个成员分别处理,再把结果合并起来。只需要把推断逻辑改成泛型形式就行:
// 先定义一个泛型类型,专门处理单个函数类型的参数提取 type ExtractParam<T> = T extends (a: infer U) => void ? U : never; // 再用这个泛型处理函数联合类型 type TypeOfAllData = ExtractParam<ArrayOfItem[number]>; // 结果为 "type_a" | "type_b"
泛型参数T会自动遍历联合类型的每个成员,分别推断出"type_a"和"type_b",最后把这两个结果合并成联合类型,这样就得到我们预期的结果啦!
备注:内容来源于stack exchange,提问作者Ali Akbar Azizi

