TypeScript打破循环引用:如何声明带父节点校验的TREE常量
解决TypeScript循环引用并保留父节点校验的方案
方案1:提前定义树的键类型
先明确树中所有节点的键名,以此为基础定义Element类型,彻底切断循环依赖:
// 先定义所有节点的键类型 type TreeKeys = 'a' | 'b'; // 基于键类型定义Element type Element = null | { parent: TreeKeys }; // 声明TREE并约束类型 const TREE = { 'a': null, 'b': { parent: 'a' }, } satisfies Record<TreeKeys, Element>;
后续添加节点时,只需更新TreeKeys类型即可,TypeScript会自动校验parent的合法性:
type TreeKeys = 'a' | 'b' | 'c'; const TREE = { 'a': null, 'b': { parent: 'a' }, 'c': { parent: 'b' }, // 合法,符合校验 } satisfies Record<TreeKeys, Element>;
方案2:泛型函数自动推导约束
如果不想手动维护键类型,用泛型函数可以动态推导树的键集合,同时约束parent必须是树中已存在的键:
function createTree<T extends Record<string, null | { parent: keyof T }>>(tree: T) { return tree; } // 初始化树,自动校验合法性 const TREE = createTree({ 'a': null, 'b': { parent: 'a' }, }); // 新增节点时自动校验 const TREE2 = createTree({ 'a': null, 'b': { parent: 'a' }, 'c': { parent: 'b' }, // 合法 // 'd': { parent: 'x' }, // 直接报错:'x'不是当前树的键 });
原代码报错原因
原代码中Element依赖typeof TREE的键,而TREE又通过satisfies约束为Record<string, Element>,形成了循环类型依赖:TypeScript解析Element时需要知道TREE的类型,解析TREE的类型又需要先确定Element的定义,因此触发循环引用错误。
内容的提问来源于stack exchange,提问作者Andrea Azzini
相关产品推荐
相关产品推荐

