下面的代码在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'.
});
}
}
答案 0 :(得分:2)
发生错误是因为control flow analysis is difficult,尤其是当您要跟踪的信息无法在类型系统中表示时。
在一般情况下,编译器实际上无法弄清楚函数接受可变变量的回调时会发生什么。这些函数内部的控制流程是一个完全的谜。也许回调函数将立即被调用一次。也许永远不会调用该回调。也许回调将被调用一百万次。或者,也许它会在不久的将来被异步调用。由于一般情况是如此绝望,因此编译器甚至没有真正尝试过。它使用了一些启发式方法,这些方法在很多情况下都有效,并且在很多情况下也必然会失败。
您选择了其中一项失败。
这里使用的启发式方法是在回调内部,重置在较宽范围内发生的所有缩小。对于这样的代码,这可以做一些合理的事情:
// 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;
编译器不知道何时或是否调用() => a.charAt(0)
。如果在调用mysteryCallbackCaller()
时立即调用它,那么将定义a
。但是,如果稍后再调用它,a
可能是不确定的。由于编译器不能保证此处的安全性,因此会报告错误。
那么,在您的情况下,我们该如何解决这个问题?我可以想到两个主要解决方案。一种是只是告诉编译器这是错误的,并且您确定将定义obj
。可以使用!
non-null assertion operator:
map2.forEach(v => {
obj!.field1 += "," + v; // okay now
});
这没有编译时错误。此解决方案的警告是,确保定义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
});
我在这里所做的全部工作是将obj
分配给constObj
。但是在进行此分配时,obj
不能为undefined
。因此constObj
只是TestIF
,而不是TestIF | undefined
。而且由于constObj
从未被重新分配,也无法undefined
,因此其余代码可以正常工作。
好的,希望能有所帮助。祝好运!
答案 1 :(得分:0)
地图的get
方法定义为Map<string, TestIF>.get(key: string): TestIF | undefined
,因此当您设置obj
时,其类型为TestIF | undefined
。
在obj
块中(重置)if
的类型时,它的作用域不同。当您在obj
内阅读forEach
时,它也在另一个范围内。 TypeScript编译器无法在更改的范围内建立正确的类型。
考虑此(有效)代码:
const key = 'mapkey';
let obj: TestIF; // Create variable with a Type
if (map1.has(key)) { // We know (with certainty) that obj exists!
obj = map1.get(key) as TestIF; // We use 'as' because we know it can't be Undefined
} else {
obj = { field1: 'testtest' };
map1.set(key, obj);
}
即使Map.get()总是返回V | undefined
,当我们使用as
时,我们还是强制TypeScript将其视为V
。我谨慎使用as
,但是在这种情况下,我们通过调用Map.has()
来检查它的存在,从而知道它的存在。
我还要强调一点,(obj === undefined)
比(obj == null)
好得多,后者只检查虚假性。 [more info]
答案 2 :(得分:0)
我现在也遇到了同样的问题,对我来说,解决方案是(适应您的情况):
if (!obj) {
return ( ... );
}