问题归结于
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
}
}