TypeScript中!操作符与in操作符结合使用的报错解决方法
解决strictNullChecks模式下!与in操作符结合的类型收窄问题
我明白你在开启strictNullChecks选项后,处理类似Redux Action payload这类可选联合类型时遇到的困扰——当把非空断言!和in类型收窄操作符结合使用时,TypeScript无法正确识别类型,导致报错。咱们先看一下你的代码和错误信息:
代码示例
interface Interface { AB?: { a: number; } | { b: number[]; } } function test(ab: Interface['AB']) { const ab2 = ('a' in ab!) ? [ab!.a] : ab!.b; // 报错:Property 'a' does not exist on type '{ a: number; } | { b: number[]; }' }
问题原因
TypeScript的类型收窄是基于单个变量的跟踪逻辑。你每次使用ab!时,它都会被视为一个独立的表达式,TypeScript无法将'a' in ab!的检查结果,关联到后续ab!.a或ab!.b中的ab!上,自然无法正确完成类型收窄。
解决方法
这里有几个实用的方案,你可以根据场景选择:
1. 先将断言后的变量赋值给新变量(最简单直接)
把ab!一次性赋值给一个新变量,TypeScript会对这个变量进行完整的类型收窄跟踪:
interface Interface { AB?: { a: number; } | { b: number[]; } } function test(ab: Interface['AB']) { const nonNullAb = ab!; // 先一次性断言为非空 const ab2 = ('a' in nonNullAb) ? [nonNullAb.a] : nonNullAb.b; // 类型收窄生效,无报错 }
2. 使用自定义类型守卫(更严谨,避免断言)
如果你不想依赖非空断言!,可以写自定义类型守卫来同时处理undefined和联合类型的判断,代码更安全:
interface Interface { AB?: { a: number; } | { b: number[]; } } // 类型守卫:判断是否是包含a属性的类型 function isTypeWithA(obj: Interface['AB']): obj is { a: number } { return obj !== undefined && 'a' in obj; } // 类型守卫:判断是否是包含b属性的类型 function isTypeWithB(obj: Interface['AB']): obj is { b: number[] } { return obj !== undefined && 'b' in obj; } function test(ab: Interface['AB']) { let ab2: number[]; if (isTypeWithA(ab)) { ab2 = [ab.a]; } else if (isTypeWithB(ab)) { ab2 = ab.b; } else { // 处理ab为undefined的情况,比如给默认值或抛出错误 ab2 = []; } }
3. 利用可选链与空值合并(适合容忍undefined的场景)
如果你的业务逻辑允许ab为undefined时返回默认值,可以用可选链简化:
interface Interface { AB?: { a: number; } | { b: number[]; } } function test(ab: Interface['AB']) { const ab2 = 'a' in (ab ?? {}) ? [ab?.a ?? 0] : ab?.b ?? []; }
不过这种方式需要额外处理空值情况,适合对默认值有明确要求的场景。
内容的提问来源于stack exchange,提问作者left click
相关产品推荐
相关产品推荐

