If 语句中的 Typescript 类型推断不能用作变量

Ita*_*nor 5 typescript

考虑以下示例:

class A {}
class B extends A {
    foo() {
        return 6;
    }
}

const variable: A = new B();

const isValid = variable instanceof B;

if (isValid) {
    variable.foo();
}
Run Code Online (Sandbox Code Playgroud)

调用.foo()时提示如下错误:

在此输入图像描述

这是有道理的,因为variable它的类型是A。但仅当是 的实例variable.foo()时才会运行。variableB

这样做时不会出现此问题:

class A {}
class B extends A {
    foo() {
        return 6;
    }
}

const variable: A = new B();

if (variable instanceof B) {
    variable.foo();
}
Run Code Online (Sandbox Code Playgroud)

为什么条件存储在变量中而不是显式写入变量中很重要if?

jca*_*alz 2

TS4.4 更新:

TypeScript 4.4 将引入对别名条件的控制流分析的支持。这意味着您有时可以将类型保护的结果保存到consts 中并在以后使用它们。因此,您上面的代码将按预期工作,无需修改。

Playground 代码链接


TS4.4 之前的答案:

microsoft/TypeScript#12184 (除其他外,请参阅该问题以获取指向它们的链接)有一个建议 ,允许将类型保护的结果存储到布尔变量中以供以后使用。虽然大多数人都同意拥有这个就好了,但该语言的首席架构师说:

这需要我们跟踪一个变量的特定值对其他变量意味着什么影响,这会给控制流分析器增加大量的复杂性(以及相关的性能损失)。尽管如此,我们仍会将其保留为建议。


为了扩展这一点,目前编译器认识到,在variable instanceof B已经测试和评估为 的代码块内true, 的类型variable 可以缩小为B... 但在该范围之外,编译器可以“忘记”这样的缩小:

if (variable instanceof B) {
    variable.foo(); // okay
}
variable.foo(); // error
Run Code Online (Sandbox Code Playgroud)

为了使类似以下内容的工作正常进行:

const isValid = variable instanceof B;

if (isValid) {
  variable.foo(); 
}
Run Code Online (Sandbox Code Playgroud)

不允许编译器“忘记”缩小范围,直到isValid它本身......或者任何依赖的内容isValid超出范围。表面上这似乎是合理的,但对于像这样的您需要这样的内存的情况,似乎在很多情况下不需要额外的工作。例如,如果你有这个:

const bar = Math.random() < 0.5 ? { a: 123, b: 456 } : undefined;
const baz = bar?.a;
Run Code Online (Sandbox Code Playgroud)

编译器是否应该“记住”这baz将是一个number当且仅当bar是一个{a: number, b: number}?也许......但除非后来有人真正使用这个事实,如:

const qux = typeof baz === "number" ? bar.b : 789;
Run Code Online (Sandbox Code Playgroud)

那么跟踪它只是浪费精力。


将来有人可能会设计出一种比当前情况更好的方法来做到这一点,同时又不会使控制流分析变得过于昂贵。也许想要在boolean变量上进行这种行为的人可以用类型谓词之类的东西手动注释它,正如有关相关问题的评论中提到的那样?

const isValid: variable is B = variable instanceof B; // not valid syntax now
Run Code Online (Sandbox Code Playgroud)

但目前,它还不是语言的一部分。如果您确实对此有强烈的感受,您可能需要转到相关的 GitHub 问题并向他们提供您的或描述您的用例,并解释为什么当前使用非存储类型保护的行为是不够的。如果有足够多的人这样做,就会增加最终实施某件事的机会。不过我不会指望它。


在可预见的未来,您最好立即使用类型保护,而不是尝试保存它们以供以后使用。接近这种“延迟”行为的一种方法是重构,以便您保存的内容比以下内容更具功能性boolean:

const varIfValid = variable instanceof B ? variable : undefined;

if (varIfValid) {
  varIfValid.foo(); // okay
}
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为varIfValidis 是一个比isB | undefined更直接与调用相关的联合。foo()true | false


Playground 代码链接