我使用的是 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
}
}
最佳答案
问题归结为 checker 中 checkSwitchStatement
中的这段代码,自 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
的类型是某种类型参数 T
受 string
,我们希望能够将它与"x"
进行比较; T
和 "x"
都不知道可以分配给另一个,但是 T
可能包含 "x"
.因此,当比较的一侧是文字并集而另一侧不是时,代码会将作为文字并集的一侧替换为基础原始类型。这种情况在您的代码中触发,其中 "x"
是文字,而 T["type"]
本身不是文字,尽管它是 constrained 通过文字的联合。
我认为我们应该提出一个问题,建议您的代码应该给出一个编译错误。在我写完那句话之后,我看到 artem 提出了一个问题,所以我将在那里添加我的分析。
如果你认为你之前出错了,也许你在想下面的代码。如果可能,根据类型参数的约束,急切地解析类型参数上的属性访问类型,因此 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
}
}
关于javascript - typescript 标记联合未在 switch 语句中进行类型检查,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52087511/