TypeScript中递归定义Symbol父类型链至BookClass的实现问询
解决TypeScript递归父类型链的类型定义问题
我有一套自定义的层级树形结构,所有节点都继承自BaseSymbol基类,每个子类都有parent属性指向父节点。需要实现getParentList函数,传入任意子类实例后,返回从该节点到根节点BookClass的所有父节点组成的数组,并且返回值的类型要精确涵盖所有可能的父节点类型。
尝试用递归类型表达这个类型链时,遇到了像Section这种parent包含自身的递归场景,会导致类型无限循环,无法正常定义。
原代码示例
// 自定义基类,避免和内置Symbol冲突 class BaseSymbol {} class BookClass extends BaseSymbol {} class Sentence extends BaseSymbol { parent: Paragraph } class Paragraph extends BaseSymbol { parent: Section } class Section extends BaseSymbol { parent: BookClass | Section | Chapter } class Chapter extends BaseSymbol { parent: BookClass } // 期望:传入Paragraph实例时,返回类型为(Paragraph | Section | Chapter | BookClass)[] function getParentList<T extends BaseSymbol>(object: T): Parentize<T>[] { const list: Parentize<T>[] = [] list.push(object) let current: T | BaseSymbol = object let parent = current.parent while (parent && !(parent instanceof BookClass)) { list.push(parent) current = parent as T parent = current.parent } if (parent) list.push(parent) return list }
解决方案:带终止条件的递归条件类型
要处理递归父类型的问题,我们可以用带终止条件的递归条件类型,结合累积类型参数来收集所有可能的父节点类型,同时避免无限循环。核心思路是:
- 每次递归时,把当前节点的
parent类型加入累积集合 - 当遇到
BookClass或者当前类型已经在累积集合中时,终止递归(避免循环)
类型定义实现
// 辅助类型:递归收集父类型链,Acc用于累积已收集的类型 type ParentizeChain<T extends BaseSymbol, Acc extends BaseSymbol = T> = // 终止条件1:当前节点的父节点是根节点BookClass,返回所有累积类型+BookClass T['parent'] extends BookClass ? Acc | BookClass // 终止条件2:当前节点的父节点已经在累积集合中(循环),返回累积类型 : T['parent'] extends Acc ? Acc // 递归:将父节点加入累积集合,继续处理父节点的父节点 : ParentizeChain<T['parent'], Acc | T['parent']> // 最终对外暴露的类型,即所有可能的节点类型联合 type Parentize<T extends BaseSymbol> = ParentizeChain<T>
修正后的getParentList函数
function getParentList<T extends BaseSymbol>(object: T): Parentize<T>[] { const list: Parentize<T>[] = [] let current: T | BaseSymbol = object list.push(current) while (true) { const parent = current.parent if (!parent) break list.push(parent) if (parent instanceof BookClass) break // 类型断言:确保父节点属于已收集的类型范围 current = parent as Parentize<T> } return list }
效果验证
// 测试1:传入Paragraph实例 const para = new Paragraph() const paraParents = getParentList(para) // paraParents的类型自动推导为:(Paragraph | Section | Chapter | BookClass)[] // 测试2:传入Section实例 const section = new Section() const sectionParents = getParentList(section) // sectionParents的类型自动推导为:(Section | Chapter | BookClass)[]
内容的提问来源于stack exchange,提问作者Pete
相关产品推荐
相关产品推荐

