本文旨在解决 TypeScript 中递归获取类字段属性时遇到的 "Type instantiation is excessively deep and possibly infinite" 错误。通过修改类型定义,特别是针对 `Map` 类型的特殊处理,以及保留字段的可选性,提供了一种避免无限递归并正确获取字段类型的方法。
在 TypeScript 中,使用递归类型定义来处理复杂的数据结构是很常见的。然而,不当的递归类型定义可能导致编译器报错,提示 "Type instantiation is excessively deep and possibly infinite"。这个问题通常发生在类型定义过于复杂,导致编译器无法在合理的深度内完成类型推断时。本文将探讨如何解决这个问题,并提供一个具体的示例,展示如何递归地获取类字段的属性,同时排除函数类型,并保留字段的可选性。
"Type instantiation is excessively deep and possibly infinite" 错误通常是由于类型定义中的循环依赖造成的。例如,一个类型 A 的定义依赖于类型 B,而类型 B 的定义又依赖于类型 A。如果这种依赖关系没有明确的终止条件,编译器就会陷入无限递归,最终报错。
在处理类字段属性时,如果类包含嵌套的复杂类型(例如,数组、Map、Set 等),并且递归类型定义没有针对这些类型进行特殊处理,就很容易触发这个错误。
为了解决这个问题,我们需要改进递归类型定义,使其能够正确处理各种类型,并避免无限递归。以下是一种可行的解决方案:
以下是一个改进后的代码示例,展示了如何递归地获取类字段的属性,同时排除函数类型,并保留字段的可选性:
type DeepWritablePrimitive = undefined | null | boolean | string | number | Function; type DeepWritable= | T extends DeepWritablePrimitive ? T : T extends (infer U)[] ? DeepWritable[] : T extends Map ? ( T extends Map ? Map > : never ) : T extends Set ? Set > : DeepWritableRecord ; type WritableKeys = { [P in keyof T]: T[P] extends Function ? never : P }[keyof T]; type DeepWritableRecord = { // need to keep optionality [K in keyof Pick >]: DeepWritable } class Base { set(data?: Partial >) { Object.assign(this, data); } } class Parent extends Base { name?: string; arr?: Parent[]; map?: Map ; }; const record = new Parent(); record.set({ // https://github.com/microsoft/TypeScript/issues/34933 arr: [{ name: '0' }], map: new Map([['key', {name: 'map_value'}]]) }) console.log(record.arr); console.log(record.map?.get('key')?.name);
在这个示例中,DeepWritable 类型定义递归地处理了数组、Map 和 Set 类型,
并保留了字段的可选性。WritableKeys 用于排除函数类型的字段。
通过改进递归类型定义,我们可以解决 TypeScript 中 "Type instantiation is excessively deep and possibly infinite" 错误,并正确地获取类字段的属性。关键在于限制递归深度,特殊处理 Map 类型,保留字段的可选性,并明确终止条件。希望本文能够帮助你更好地理解和解决这个问题。