如何在TypeScript中正确编写递归克隆函数的类型签名?
解决TypeScript递归类型克隆函数的类型不匹配问题
问题分析
你写的clone函数报错,核心原因是泛型T是Cloneable的子类型:当参数是数组时,arg.map(e => clone(e))返回的类型是Cloneable[],但T可能是更具体的数组类型(比如string[]),TypeScript无法确认Cloneable[]就是T,因此抛出类型不兼容的错误。
解决方案
通过递归条件类型精确推导输入与输出的类型关系,同时用类型断言辅助TypeScript完成类型匹配,无需使用any:
写法一:拆分基础类型与递归类型
// 定义基础可克隆类型 type BaseCloneable = string | number; // 递归定义可克隆类型(基础类型或其数组) type Cloneable = BaseCloneable | Cloneable[]; function clone<T extends Cloneable>(arg: T): T extends BaseCloneable ? T : Clone<T>[] { if (Array.isArray(arg)) { return arg.map(clone) as T; } else { return arg; } } // 辅助递归类型,处理数组元素的克隆类型 type Clone<T> = T extends BaseCloneable ? T : Clone<T>[];
写法二:直接用条件类型推断数组元素
type Cloneable = string | number | Cloneable[]; function clone<T extends Cloneable>(arg: T): T extends (infer U)[] ? Clone<U>[] : T { if (Array.isArray(arg)) { return arg.map(clone) as T; } else { return arg; } } // 递归推导克隆后的类型 type Clone<U> = U extends Cloneable ? (U extends (infer V)[] ? Clone<V>[] : U) : never;
原理说明
- 用条件类型判断输入类型
T:如果是数组,就递归推导其元素的克隆类型,返回对应的数组类型;如果是基础类型,直接返回原类型。 - 类型断言
as T是安全的,因为我们的条件类型已经保证了arg.map(clone)的返回类型和T完全匹配,TypeScript只是无法自动推导这层关系,需要我们明确告知。
内容的提问来源于stack exchange,提问作者recursive
相关产品推荐
相关产品推荐

