类型守卫的改善:别名条件表达式和判别式的控制流分析

527 阅读1分钟

本系列作为《TypeScript 入门实战笔记》课程(见拉勾教育)的补丁,随时更新。

类型守卫的改善:别名条件表达式和判别式的控制流分析

原文:Control flow analysis of aliased conditional expressions and discriminants #44730

这是 TypeScript 大佬 ahejlsberg 新近提的一个 PR,意味着不久的将来,大概率这个 feat 将在新版本里出现。

// 援引自官方示例
function f7(x: string | number | boolean) {
    const isString = typeof x === "string";
    const isNumber = typeof x === "number";
    const isStringOrNumber = isString || isNumber;
    if (isStringOrNumber) {
        x;  //  C1 string | number
    }
    else {
        x;  // C2 boolean
    }
}

如以上示例所示,在目前的正式版本里(4.2.*),注释 C1、C2 处,变量 x 的类型并不会缩小成响应的 string | number 和 boolean,这也是被广泛吐槽的。

当然 C1、C2 处类型缩小是符合我们预期(合理)的,但这就需要 TypeScript 引擎做额外的代码分析(计算量、复杂度、成本)。所以大佬的这个 PR 里解决并仅解决了分析成本不那么高的场景,比如上边示例里的 const,以及直接和间接混合的条件表达式。

// 援引自官方表达式
function f8(value: number | undefined) : number {
    const isNumber = typeof value == "number";
    if (isNumber && value > 0) {
        return value * 10;
    }
    return 0;
}

但对于 let 或者其他比较复杂(难以追踪、分析类型条件,包括需要计算才能获得类型的)的情况,则无法做到类型缩小。

// 援引自官方示例
function f5(x: unknown) {
    let isString = typeof x === 'string';
    if (isString) {
        x.length;  // Error, no narrowing because isString isn't 'const'
    }
}

type Data = { kind: 'str', payload: string } | { kind: 'num', payload: number };

function foo({ kind, payload }: Data) {
    if (kind === 'str') {
        payload.length;  // Error, payload not narrowed to string
    }
}