TypeScript中undefined类型守卫失效?技术问题问询
一、类型收窄后仍提示类型不兼容
代码示例
interface B { } interface A { b: B | undefined; } function f1(a: A) { if (a.b) { return f2(a); } } function f2(a: { b: B }) { }
报错信息
Argument of type 'A' is not assignable to parameter of type '{ b: B; }'.
Types of property 'b' are incompatible.
Type 'B | undefined' is not assignable to type 'B'.
Type 'undefined' is not assignable to type 'B'.
'arg' is declared but its value is never read.
问题原因
TypeScript的类型收窄仅作用于单次属性访问的结果,而非整个对象的类型。if (a.b)只能证明当前访问a.b时它是B类型,但A接口本身的b属性依然允许是B | undefined,所以TypeScript不会将整个a的类型自动收窄为{b: B}。
解决办法
- 类型断言:直接将
a断言为目标类型:f2(a as { b: B }) - 构造新对象:基于当前收窄后的属性值创建符合要求的对象:
f2({ ...a, b: a.b }) - 自定义类型守卫:编写函数确认整个对象的类型:
function isAWithB(a: A): a is { b: B } { return a.b !== undefined; } function f1(a: A) { if (isAWithB(a)) { return f2(a); } }
二、非确定性Getter导致运行时undefined报错
代码示例
interface B { i: string; } interface A { b: B | undefined; } function f1(a: A) { if (a.b) { return f2(a.b); } } function f2(b: B) { console.log(b.i); } const maybeFoo: A = { get b() { if (Math.random() > 0.5) { return { i: "foo" }; } return undefined; } }; // 测试执行 f1(maybeFoo);
运行报错
[ERR]: "Executed JavaScript Failed:"
[ERR]: Cannot read properties of undefined (reading 'i')
[LOG]: "foo"
问题原因
这里的b是Getter属性,每次访问a.b都会重新执行Getter函数。if (a.b)判断时Getter返回了B类型值,进入分支;但调用f2(a.b)时再次访问a.b,此时Getter可能返回undefined,导致运行时崩溃。TypeScript编译阶段默认假设属性访问是幂等的(多次访问结果一致),无法检测这种非确定性场景。
解决办法
将Getter的结果缓存到变量中,避免多次访问触发不同结果:
function f1(a: A) { const b = a.b; // 仅访问一次Getter,缓存结果 if (b) { return f2(b); } }
内容的提问来源于stack exchange,提问作者Dan Fritchman

