为何TypeScript已做非空检查仍提示对象可能为undefined?
TypeScript类型收窄失效问题解析
最小可复现示例
type Keys = "FOO" | "BAR"; type ThingType = { category: Keys, thingName: string; } type CategorisedThings = Partial<Record<Keys, string[]>> type ListedThings = ThingType[] const categorizeFunction = (listedThings: ListedThings) => { const result: CategorisedThings = {} for (const thing of listedThings) { if (result[thing.category] !== undefined) { // 第14行 result[thing.category].push(thing.thingName) // 第15行 } else { result[thing.category] = [thing.thingName] } } }
上述示例中,TypeScript在第15行抛出错误:Object is possibly 'undefined'.
result[thing.category].push(thing.thingName) ^^^^^^^^^^^^^^^^^^^^^^
问题原因
TypeScript的类型检查器无法精准追踪result[thing.category]在条件判断和后续访问之间的关联。因为thing.category是"FOO" | "BAR"的联合类型,虽然我们逻辑上清楚两次访问的是同一个属性,但类型系统跨语句时,无法确定thing.category的具体值没有变化——哪怕它在循环里是常量,TypeScript也没法把两次属性访问的类型收窄关联起来。
另外,CategorisedThings的类型是Partial<Record<Keys, string[]>>,这意味着每个键对应的类型都是string[] | undefined,对于联合类型键的属性访问,TypeScript的类型收窄机制无法做到精准识别,所以即便做了undefined检查,它还是会认为属性可能为undefined。
解决方案
1. 用临时变量存储属性值
把要访问的属性值存到临时变量里,TypeScript就能追踪这个变量的类型,自然不会报错:
const categorizeFunction = (listedThings: ListedThings) => { const result: CategorisedThings = {} for (const thing of listedThings) { const categoryItems = result[thing.category]; if (categoryItems !== undefined) { categoryItems.push(thing.thingName); } else { result[thing.category] = [thing.thingName]; } } }
2. 类型断言(谨慎使用)
如果能确保逻辑绝对没问题,可以用类型断言明确告诉TypeScript该属性不为undefined:
if (result[thing.category] !== undefined) { (result[thing.category] as string[]).push(thing.thingName); }
3. 提前初始化所有键
一开始就给result的所有Keys键初始化空数组,后续就不需要做undefined检查了:
const categorizeFunction = (listedThings: ListedThings) => { const result: CategorisedThings = { FOO: [], BAR: [] }; for (const thing of listedThings) { result[thing.category].push(thing.thingName); } }
内容的提问来源于stack exchange,提问作者tim-mccurrach
相关产品推荐
相关产品推荐

