部分检查中的道具,但仍未定义?

gho*_*t23 3 typescript

我使用的是 Typescript 3.7.2,但它也发生在早期的 Typescript 版本中。strictNullChecks 处于开启状态。

我有这个小代码片段:

interface Test {
    one: boolean;
    two: boolean;
}

function getPropOrFalse(someTest: Partial<Test>, prop: keyof Test): boolean {
    if (prop in someTest) {
        const value = someTest[prop];
        return value; // <-- typescript determines this still to be boolean | undefined. Why??
    }
    return false;
}
Run Code Online (Sandbox Code Playgroud)

我不明白,为什么我的常数在那个检查之后value仍然存在。有谁知道它背后的原理吗?boolean|undefinedprop in someTest

编辑:顺便说一下,更改prop in someTestsomeTest[prop] !== undefined不会改变结果。

jca*_*alz 5

这里发生了一些事情。第一的:

打字稿没有多少津贴的区别那些特性/功能参数之间缺少和那些是存在的,但undefined。“可选性”注释foo?: Bar代表这两种情况。并且,Partial<Test>计算为具有两个可选属性的类型,相当于{one?: boolean, two?: boolean}.

因此我可以称之为:

getPropOrFalse({ one: undefined }, "one"); // oops
Run Code Online (Sandbox Code Playgroud)

这就是编译器现在的方式(直到并且除非以某种方式解决microsoft/TypeScript#13195)。我可以想象跳过很多圈子来使调用签名getPropOrFalse()禁止someTest带有present-but-undefined属性的参数,但是编译器可能仍然不理解 的实现内部的含义getPropOrFalse(),因此您必须使用断言之类的。我会说不与类型系统作斗争并允许存在但undefined属性更健康。

in一个属性的存在运营商的检查,但它不检查属性的值是undefined或不是。因此,in操作符不足以检查可选属性,而typeof foo.prop === "undefined"实际检查属性是否undefined存在。(是的,我知道,不是在您的情况下;请继续阅读!)可以通过以下方式证明这种差异:

function getOneOrFalse(someOne: { one?: boolean }): boolean {
    if ("one" in someOne) {
        return someOne.one; // error!  boolean | undefined        
    }
    if (typeof someOne.one !== "undefined") {
        return someOne.one; // okay
    }
    return false;
}
Run Code Online (Sandbox Code Playgroud)

但是,显然,更改为typeof someTest[prop] === "undefined"不适用于您的getPropOrFalse()功能:

function badGetPropOrFalse(someTest: Partial<Test>, prop: keyof Test): boolean {
    if (typeof someTest[prop] !== "undefined") {
        return someTest[prop]; // still error! boolean | undefined
    }
    return false;
}
Run Code Online (Sandbox Code Playgroud)

是什么赋予了?这是第二件事:

当属性不是单个文字时,问题与尝试对索引属性访问(带括号)进行类型保护有关。在您的情况下,因为是一个变量,并且是联合类型,编译器无法(或不愿意)执行控制流分析缩小(请参阅 microsoft/TypeScript#10530)。我想有人试图解决这个问题,但它对性能产生了重大影响,因此它似乎已被放弃(或至少被推迟)。我预计性能会受到一些影响,因为为了使其正常工作,编译器必须在由变量索引时对“变量的类型”进行特定跟踪prop"one" | "two"someTestprop”,这不能简单地转化为仅缩小 的类型someTest。(毕竟,如果我有两个属性,prop1并且prop2都是 type keyof Test,并且我检查了someTest[prop1],那么它将与 的类型无关someTest[prop2],尽管事实上prop1prop2是相同的根据类型系统类型。)

解决这个问题的方法是减轻编译器跟踪按变量索引的变量的负担,并首先将其分配给一个新const变量,如另一个答案中所述:

function getPropOrFalse(someTest: Partial<Test>, prop: keyof Test): boolean {
    const val = someTest[prop]; // boolean | undefined
    if (typeof val !== "undefined") {
        return val; // okay
    }
    return false;
}
Run Code Online (Sandbox Code Playgroud)

当然,一旦我们开始像这样重构,我们就可以一路走下去:

function simplestGetPropOrFalse(someTest: Partial<Test>, prop: keyof Test): boolean {
    return someTest[prop] ?? false;
}
Run Code Online (Sandbox Code Playgroud)

(它使用新添加的空合并运算符)


好的,希望这有助于解释发生了什么。祝你好运!

代码链接