【问题标题】:Typescript Tagged union not type-checked in switch statements打字稿标记的联合未在 switch 语句中进行类型检查
【发布时间】:2018-08-29 23:54:50
【问题描述】:

我正在使用 Typescript 3.0.1。在下面的代码中,为什么第 7 行没有编译器错误?我以前曾经有过这种行为;它是从 Typescript 中取出的还是有一些奇怪的回归?

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
    }
}

【问题讨论】:

  • 看起来像个bug,我创建了this issue
  • 感谢@artem提交这个issue,我会关注的

标签: javascript typescript types switch-statement discriminated-union


【解决方案1】:

问题归结为checkercheckSwitchStatement 中的这段代码,该代码自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 语句基于双向类型“可比性”的概念,它(省略了很多与此处无关的细节)表示一侧的联合组成部分必须是可分配的到另一方的工会成员。这应该是对两侧的类型是否重叠的启发式方法。启发式方法适用于通常使用对象的方式,但不适用于原语,其中(例如)如果a 的类型是受string 约束的某个类型参数T,我们希望能够比较发给"x";已知T"x" 都不能分配给另一个,但T 可能包括"x"。因此,当比较的一侧是文字并集而另一侧不是时,代码会将作为文字并集的一侧替换为底层原始类型。这种情况会在您的代码中触发,其中"x" 是一个文字,而T["type"] 本身不是一个文字,尽管它受到文字联合的约束

我认为我们应该提出一个问题,建议您的代码应该给出编译错误。而且我写完那句话后,看到artem提出了一个issue,所以我会在那里添加我的分析。

您认为您之前遇到过错误,也许您正在考虑以下代码。类型参数上的属性访问的类型,如果可能的话,会根据类型参数的约束急切地解析,所以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
    }
}

【讨论】:

  • 是的,@McCutchen,我相信你的例子就是我之前看到的。我经常对 Typescript 推理失败所需的时间感到惊讶。我们将看看这个问题会产生什么结果。
猜你喜欢
  • 2019-10-29
  • 2020-10-16
  • 2019-05-26
  • 2019-01-15
  • 2019-11-05
  • 1970-01-01
  • 2019-03-27
  • 2021-04-09
相关资源
最近更新 更多