TypeScript递归类型别名与类型守卫使用问题:移除any类型时遇到属性访问报错
TypeScript递归类型别名与类型守卫使用问题:移除any类型时遇到属性访问报错
老哥,你的问题我太感同身受了——想用类型守卫替代any来保证类型安全,但卡在了宽泛的递归类型上,TS死活不认你访问的属性对吧?其实核心问题在于:你虽然通过Array.isArray()确认了copybookResult.result是数组,但这个数组的元素类型还是宽泛的jsonValue,TS没法确定里面的元素一定是带includedComponent和includedTotal的对象,自然会报错。
给你两个实用的解决方案,按需选择:
方案一:定义精准的业务类型(推荐)
既然你知道copybookResult和它的数组元素有固定结构,不如直接定义对应的具体类型,从源头消除类型模糊:
// 先定义数组元素的具体类型 interface CopybookItem { includedComponent: string | number; // 根据实际数据类型调整 includedTotal: number; // 这里可以添加其他你已知的属性 } // 再定义顶层结果的类型 interface CopybookResult { result: CopybookItem[]; // 顶层对象的其他属性也可以在这里明确定义 } // 给getCopybookCount指定返回类型,不用再依赖宽泛的jsonValue const copybookResult: CopybookResult = await this.getCopybookCount; // 这时候甚至不需要额外的类型守卫,TS已经能完全识别类型 if (Array.isArray(copybookResult.result)) { sortedCopybookResult = copybookResult.result.sort((a, b) => a.includedComponent > b.includedComponent ? 1 : -1 ); copybookCount = sortedCopybookResult[0].includedTotal; }
这种方式最靠谱,类型清晰,TS的推断能完全跟上,代码可读性也更高,从根源上避免了类型模糊的问题。
方案二:给数组元素加专属类型守卫
如果没法直接修改getCopybookCount的返回类型,或者需要兼容现有的jsonValue,可以给数组元素写一个专属的类型守卫,进一步缩小类型范围:
// 写一个类型守卫,判断元素是符合要求的对象 function isCopybookItem(item: jsonValue): item is { includedComponent: string | number; includedTotal: number } { // 先确认是对象,再检查必要属性是否存在 return Utility.isObject(item) && typeof (item as any).includedComponent !== 'undefined' && typeof (item as any).includedTotal !== 'undefined'; } // 在代码里先过滤出符合条件的元素 if (Utility.isObject(copybookResult) && Array.isArray(copybookResult.result)) { // 过滤后validItems的类型会被TS识别为符合要求的对象数组 const validItems = copybookResult.result.filter(isCopybookItem); sortedCopybookResult = validItems.sort((a, b) => a.includedComponent > b.includedComponent ? 1 : -1 ); // 记得加非空判断,避免数组为空时出现报错 if (validItems.length > 0) { copybookCount = sortedCopybookResult[0].includedTotal; } }
这个方案的好处是不改动原有宽泛的jsonValue类型,通过局部的类型守卫来缩小范围,适合需要兼容多种数据结构的场景。
总结一下:TS的类型推断是「渐进式缩小范围」的,你原来的判断只缩小了顶层对象和数组的范围,但数组元素的类型还是太杂,必须再进一步缩小到具体的对象类型,TS才会允许你访问专属属性。
备注:内容来源于stack exchange,提问作者djacks
相关产品推荐
相关产品推荐

