Skip to content

Commit 7c1b28f

Browse files
committed
Allow primitive type guards with typeof on right
Previously, only type guards of the form `typeof x === 'string'` were allowed. Now you can write `'string' === typeof x`.
1 parent 6b8109a commit 7c1b28f

2 files changed

Lines changed: 24 additions & 9 deletions

File tree

src/compiler/binder.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -612,7 +612,7 @@ namespace ts {
612612
if (isNarrowingExpression(expr.left) && (expr.right.kind === SyntaxKind.NullKeyword || expr.right.kind === SyntaxKind.Identifier)) {
613613
return true;
614614
}
615-
if (expr.left.kind === SyntaxKind.TypeOfExpression && isNarrowingExpression((<TypeOfExpression>expr.left).expression) && expr.right.kind === SyntaxKind.StringLiteral) {
615+
if (isTypeOfNarrowingBinaryExpression(expr)) {
616616
return true;
617617
}
618618
return false;
@@ -624,6 +624,20 @@ namespace ts {
624624
return false;
625625
}
626626

627+
function isTypeOfNarrowingBinaryExpression(expr: BinaryExpression) {
628+
let typeOf: Expression;
629+
if (expr.left.kind === SyntaxKind.StringLiteral) {
630+
typeOf = expr.right;
631+
}
632+
else if (expr.right.kind === SyntaxKind.StringLiteral) {
633+
typeOf = expr.left;
634+
}
635+
else {
636+
typeOf = undefined;;
637+
}
638+
return typeOf && typeOf.kind === SyntaxKind.TypeOfExpression && isNarrowingExpression((<TypeOfExpression>typeOf).expression);
639+
}
640+
627641
function createBranchLabel(): FlowLabel {
628642
return {
629643
flags: FlowFlags.BranchLabel,

src/compiler/checker.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7864,7 +7864,8 @@ namespace ts {
78647864
if (isNullOrUndefinedLiteral(expr.right)) {
78657865
return narrowTypeByNullCheck(type, expr, assumeTrue);
78667866
}
7867-
if (expr.left.kind === SyntaxKind.TypeOfExpression && expr.right.kind === SyntaxKind.StringLiteral) {
7867+
if (expr.left.kind === SyntaxKind.TypeOfExpression && expr.right.kind === SyntaxKind.StringLiteral ||
7868+
expr.left.kind === SyntaxKind.StringLiteral && expr.right.kind === SyntaxKind.TypeOfExpression) {
78687869
return narrowTypeByTypeof(type, expr, assumeTrue);
78697870
}
78707871
break;
@@ -7897,12 +7898,12 @@ namespace ts {
78977898
function narrowTypeByTypeof(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type {
78987899
// We have '==', '!=', '====', or !==' operator with 'typeof xxx' on the left
78997900
// and string literal on the right
7900-
const left = <TypeOfExpression>expr.left;
7901-
const right = <LiteralExpression>expr.right;
7902-
if (!isMatchingReference(reference, left.expression)) {
7901+
const typeOf = <TypeOfExpression>(expr.left.kind === SyntaxKind.TypeOfExpression ? expr.left : expr.right);
7902+
const literal = <LiteralExpression>(expr.right.kind === SyntaxKind.StringLiteral ? expr.right : expr.left);
7903+
if (!isMatchingReference(reference, typeOf.expression)) {
79037904
// For a reference of the form 'x.y', a 'typeof x === ...' type guard resets the
79047905
// narrowed type of 'y' to its declared type.
7905-
if (containsMatchingReference(reference, left.expression)) {
7906+
if (containsMatchingReference(reference, typeOf.expression)) {
79067907
return declaredType;
79077908
}
79087909
return type;
@@ -7915,14 +7916,14 @@ namespace ts {
79157916
// We narrow a non-union type to an exact primitive type if the non-union type
79167917
// is a supertype of that primtive type. For example, type 'any' can be narrowed
79177918
// to one of the primitive types.
7918-
const targetType = getProperty(typeofTypesByName, right.text);
7919+
const targetType = getProperty(typeofTypesByName, literal.text);
79197920
if (targetType && isTypeSubtypeOf(targetType, type)) {
79207921
return targetType;
79217922
}
79227923
}
79237924
const facts = assumeTrue ?
7924-
getProperty(typeofEQFacts, right.text) || TypeFacts.TypeofEQHostObject :
7925-
getProperty(typeofNEFacts, right.text) || TypeFacts.TypeofNEHostObject;
7925+
getProperty(typeofEQFacts, literal.text) || TypeFacts.TypeofEQHostObject :
7926+
getProperty(typeofNEFacts, literal.text) || TypeFacts.TypeofNEHostObject;
79267927
return getTypeWithFacts(type, facts);
79277928
}
79287929

0 commit comments

Comments
 (0)