TypeScript无递归条件映射类型NoOp等效原类型的原理探究
TypeScript 递归泛型NoOp的底层机制解析
先看示例代码:
// 等效于T type NoOp<T> = { [P in keyof T]: NoOp<T[P]>; }; // 使用示例: type ObjType = { a: string, b: { c: number, d: boolean} }; type NoOpObj = NoOp<ObjType>; // 等效于ObjType type NoOpString = NoOp<string>; // string
一、为什么看似无条件递归却不报错?
核心原因是TypeScript的类型惰性求值和循环引用自动终止机制:
- TypeScript的类型系统是按需解析的,只有当类型被实际用于类型断言、变量声明、函数参数/返回值标注等场景时,才会触发类型展开。如果只是定义类型而不使用,递归逻辑根本不会执行。
- 当类型被触发解析时,TypeScript会自动检测递归过程中的循环引用。比如处理
NoOp<ObjType>时,递归到NoOp<string>、NoOp<number>这类原始类型后,继续递归的结果不会产生新的类型(原始类型的属性方法递归后最终还是回到原始类型或内置固定类型),此时TypeScript会直接终止递归,避免无限循环报错。
二、为什么NoOp等于string本身?
这涉及TypeScript的类型等价规则和原始类型的特殊处理:
- 虽然
keyof string能拿到concat、slice等原型方法,但当对原始类型使用映射类型{[P in keyof T]: ...}时,TypeScript会将生成的“对象包装类型”和原始类型视为完全等价。比如{[P in keyof string]: string[P]}和string在类型检查中没有任何区别。 - 当递归到原始类型时,TypeScript会直接短路递归返回原类型。因为继续递归原始类型的属性(比如
NoOp<typeof string.prototype.concat>),最终得到的类型不会改变原始类型的本质,递归失去意义,因此TypeScript会直接终止递归,让NoOp<string>等于string。
内容的提问来源于stack exchange,提问作者aldel
相关产品推荐
相关产品推荐

