代码之家  ›  专栏  ›  技术社区  ›  prmph

Typescript标记的union未在switch语句中键入checked

  •  2
  • prmph  · 技术社区  · 6 年前

    我正在使用TypeScript3.0.1。在下面的代码中,为什么第7行没有编译器错误?我以前也有过这种行为,是从字体中去掉的,还是有一些奇怪的回归?

    type A = {type :"a"}
    type B = {type :"b"}
    type Any = A | B
    
    function get<T extends Any>(x: T["type"]): T|undefined {
        switch (x) {
            case "x": return undefined
            default: return undefined
        }
    }
    
    1 回复  |  直到 6 年前
        1
  •  1
  •   Matt McCutchen    6 年前

    问题归结于 checkSwitchStatement checker ,从2016年开始:

                    let caseType = checkExpression(clause.expression);
                    const caseIsLiteral = isLiteralType(caseType);
                    let comparedExpressionType = expressionType;
                    if (!caseIsLiteral || !expressionIsLiteral) {
                        caseType = caseIsLiteral ? getBaseTypeOfLiteralType(caseType) : caseType;
                        comparedExpressionType = getBaseTypeOfLiteralType(expressionType);
                    }
                    if (!isTypeEqualityComparableTo(comparedExpressionType, caseType)) {
                        // expressionType is not comparable to caseType, try the reversed check and report errors if it fails
                        checkTypeComparableTo(caseType, comparedExpressionType, clause.expression, /*headMessage*/ undefined);
                    }
    

    (有类似的代码会影响直接比较 x === "x" .)

    规则 a === b 而类似的switch语句是基于双向类型“可比性”的概念,该概念(省略了许多与此无关的细节)表示,一方的union成分必须可分配给另一方的union成分。这应该是一个启发式的方法来判断两边的类型是否重叠。启发式对于通常使用对象的方式很有效,但对于原语不太有效,例如,如果 a 是某个类型参数 T 约束条件 string ,我们希望能够将其与 "x" ;也不是 T型 也不是 “x” 已知可转让给另一方,但 T型 可能包括 “x” . 因此,当比较的一方是文本的并集,而另一方不是时,代码将用基础原语类型替换作为文本并集的一方。在你的代码中触发了这个案子 “x” 是字面上的 T["type"] 不是字面意思,尽管它是 约束的 通过文字的结合。

    我认为我们应该提出一个问题,建议您的代码应该给出一个编译错误。在我写了那句话之后,我看到阿泰姆提出了一个问题,所以我会在这里补充我的分析。

    你相信你之前有个错误,也许你在想下面的代码。如果可能的话,可以根据类型参数的约束急切地解决对类型参数的属性访问的类型,因此 y.type 被认为具有类型 "a" | "b" ,并且比较的两边都是文字类型的并集,因此特殊情况不适用。

    type A = {type :"a"}
    type B = {type :"b"}
    type Any = A | B
    
    function get<T extends Any>(y: T): T | undefined {
        switch (y.type) {
            case "x": return undefined
            default: return undefined
        }
    }