“对象可能是'未定义'。” 在打字稿中

N.F*_*.F. 6 typescript

下面的代码引发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)

我在这里所做的只是分配objconstObj. 但是在此分配发生时,obj不能是undefined。因此constObj只是 a TestIF,而不是 a TestIF | undefined。并且由于constObj永远不会被重新分配并且不能被重新分配undefined,其余的代码都可以工作。


好的,希望有帮助。祝你好运!

代码链接

  • 如果能够指示打字稿内部作用域内的值遵循同步流,而不是使用“!”完全抑制,从而破坏类型保护,那么将会很有用。 (2认同)