下面的代码引发Object is possibly 'undefined'.
的obj.field1 += ',' + v;
。TypeScript 表示obj
可能未定义,但此时obj
不能未定义,因为{field1: 'testtest'}
在map.get(key)
返回未定义的情况下已分配。
为什么我收到这个错误?我怎样才能解决这个问题?
interface TestIF {
field1: string;
}
export class MyClass {
test(): void {
const map1: Map<string, TestIF> = new Map();
const map2: Map<string, string> = new Map();
const key = 'mapkey';
let obj = map1.get(key);
if (obj == null) {
obj = {field1: 'testtest'};
map1.set(key, obj);
}
map2.forEach( v => {
obj.field1 += ',' + v; // Object is possibly 'undefined'.
});
}
}
Run Code Online (Sandbox Code Playgroud)
jca*_*alz 11
发生错误是因为控制流分析很困难,尤其是当您要跟踪的信息无法在类型系统中表示时。
在一般情况下,编译器真的无法弄清楚当函数接受改变变量的回调时会发生什么。这些函数内部的控制流完全是个谜;也许回调将被立即调用并且恰好一次。也许永远不会调用回调。也许回调会被调用一百万次。或者它可能会在很远的将来被异步调用。由于一般情况是如此无望,编译器甚至没有真正尝试。它使用了一些适用于很多情况的启发式方法,并且在很多情况下也必然会失败。
你选择了失败之一。
这里使用的启发式是在回调内部,所有发生在更广泛范围内的缩小都被重置。这对像这样的代码做了合理的事情:
// who knows when this actually calls its callback?
declare function mysteryCallbackCaller(cb: () => void): void;
let a: string | undefined = "hey";
mysteryCallbackCaller(() => a.charAt(0)); // error! a may be undefined
a = undefined;
Run Code Online (Sandbox Code Playgroud)
编译器不知道何时或是否() => a.charAt(0)
被调用。如果它在被调用时立即mysteryCallbackCaller()
被调用,a
则将被定义。但是如果稍后调用它,则a
可能是未定义的。由于这里编译器不能保证安全,所以报错。
那么我们可以做些什么来解决您的案例中的这个问题?我能想到的主要解决方案有两种。一种方法是告诉编译器它是错误的,并且您确定obj
它将被定义。这可以使用!
非空断言运算符来完成:
map2.forEach(v => {
obj!.field1 += "," + v; // okay now
});
Run Code Online (Sandbox Code Playgroud)
这适用于没有编译时错误。这个解决方案的警告是,确保obj
定义的责任现在只是你的,而不是编译器的。如果您更改前面的代码并且obj
确实可能未定义,则类型断言仍将抑制错误,并且您将在运行时遇到问题。
另一种解决方案是更改您正在执行的操作,以便编译器可以验证您的回调是否安全。最简单的方法是使用一个新变量:
// over here the compiler knows obj is defined
const constObj = obj; // type is inferred as TestIF
map2.forEach(v => {
constObj.field1 += "," + v; // okay, constObj is TestIF, so this works
});
Run Code Online (Sandbox Code Playgroud)
我在这里所做的只是分配obj
给constObj
. 但是在此分配发生时,obj
不能是undefined
。因此constObj
只是 a TestIF
,而不是 a TestIF | undefined
。并且由于constObj
永远不会被重新分配并且不能被重新分配undefined
,其余的代码都可以工作。
好的,希望有帮助。祝你好运!