为什么TypeScript不允许泛型中存在循环类型引用?
TypeScript 自引用类型泛型封装失效说明
如下是类型在定义中直接引用自身的示例,但该用法经过泛型抽象后会完全失效:
type a = { val: a }; // <-- 不限制循环引用! type record<T> = { val: T }; type b = record<b>; // <-- 无法正常工作! type func<T> = (arg: T) => void; type c = func<c>; // <-- 无法正常工作! type d = (arg: d) => void; // <-- 可以正常生效!?
原因说明
- TypeScript原生支持对象、函数类型在定义时直接递归引用自身,因此
type a、type d的写法可以正常通过校验,不会判定为非法循环。 - 泛型实例化的校验逻辑和直接定义类型不同:当尝试为泛型传入还在定义过程中的自身类型作为参数时,TS的循环检测机制会优先判定该写法存在非法循环引用,不会先展开泛型结构再校验,因此
type b和type c会报错。 - 函数类型的直接自引用是TS做了特殊兼容的场景,该兼容逻辑仅对直接定义的函数类型生效,封装为泛型后不会触发。
解决方案
如果需要实现泛型化的递归类型,可以通过显式将递归逻辑写到泛型内部绕开校验,示例如下:
// 递归对象泛型的正确写法 type RecursiveRecord<T> = { val: RecursiveRecord<T> } & T // 等价于直接定义的type a,还支持额外扩展属性 type b = RecursiveRecord<{}> // 递归函数泛型的正确写法 type RecursiveFunc<T> = (arg: RecursiveFunc<T>) => T // 等价于直接定义的type d type c = RecursiveFunc<void>
内容的提问来源于stack exchange,提问作者radiish
相关产品推荐
相关产品推荐

