TypeScript 的 typeof type guards 走得很好。 typeof val 是一个字符串,你可以对它进行任意字符串操作,但是typeof val === "string" 是一个特殊的构造,当表达式为true 时,它会缩小val 的类型。因此,TypeScript 被明确编程为匹配 typeof ${reference} ${op} ${literal} 和 ${literal} ${op} typeof ${reference}(对于 op = ==、!=、=== 和 !==),但 typeof ${reference} 没有内置的括号容差(是 SyntaxKind.ParenthesizedExpression 而不是 SyntaxKind.TypeOfExpression)、字符串操作或其他任何东西。
TypeScript 负责人 Ryan Cavanaugh 在 microsoft/TypeScript#42203 中描述了这一点,“typeof 类型缩小的行为与等效括号分组不同”,感谢 jcalz 提供的链接:
缩窄只发生在预定义的句法模式上,这不是其中之一。不过,为了清楚起见,我可以看到想要在此处添加括号 - 我们也应该检测到这个。
这听起来像是未来修复的候选者,尽管即使添加了该模式,您仍然会在一定程度上受限于用作类型保护的 typeof 表达式的复杂性。
来自编译器源microsoft/TypeScript main/src/compiler/checker.ts,我的cmets:
function narrowTypeByBinaryExpression(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type {
switch (expr.operatorToken.kind) {
// ...
case SyntaxKind.EqualsEqualsToken:
case SyntaxKind.ExclamationEqualsToken:
case SyntaxKind.EqualsEqualsEqualsToken:
case SyntaxKind.ExclamationEqualsEqualsToken:
const operator = expr.operatorToken.kind;
const left = getReferenceCandidate(expr.left);
const right = getReferenceCandidate(expr.right);
// Check that the left is typeof and the right is a string literal...
if (left.kind === SyntaxKind.TypeOfExpression && isStringLiteralLike(right)) {
return narrowTypeByTypeof(type, left as TypeOfExpression, operator, right, assumeTrue);
}
// ...or the opposite...
if (right.kind === SyntaxKind.TypeOfExpression && isStringLiteralLike(left)) {
return narrowTypeByTypeof(type, right as TypeOfExpression, operator, left, assumeTrue);
}
// ...or skip it and move on. Don't bother trying to remove parentheses
// or doing anything else clever to try to make arbitrary expressions work.
if (isMatchingReference(reference, left)) {
return narrowTypeByEquality(type, operator, right, assumeTrue);
}
if (isMatchingReference(reference, right)) {
return narrowTypeByEquality(type, operator, left, assumeTrue);
}
// ...
}
return type;
}