diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 8c48d984b78..bfc9b07697b 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -612,7 +612,7 @@ namespace ts { if (isNarrowingExpression(expr.left) && (expr.right.kind === SyntaxKind.NullKeyword || expr.right.kind === SyntaxKind.Identifier)) { return true; } - if (expr.left.kind === SyntaxKind.TypeOfExpression && isNarrowingExpression((expr.left).expression) && expr.right.kind === SyntaxKind.StringLiteral) { + if (isTypeOfNarrowingBinaryExpression(expr)) { return true; } return false; @@ -624,6 +624,20 @@ namespace ts { return false; } + function isTypeOfNarrowingBinaryExpression(expr: BinaryExpression) { + let typeOf: Expression; + if (expr.left.kind === SyntaxKind.StringLiteral) { + typeOf = expr.right; + } + else if (expr.right.kind === SyntaxKind.StringLiteral) { + typeOf = expr.left; + } + else { + typeOf = undefined;; + } + return typeOf && typeOf.kind === SyntaxKind.TypeOfExpression && isNarrowingExpression((typeOf).expression); + } + function createBranchLabel(): FlowLabel { return { flags: FlowFlags.BranchLabel, diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 650bc488b0e..dbc2c920dbb 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7864,7 +7864,8 @@ namespace ts { if (isNullOrUndefinedLiteral(expr.right)) { return narrowTypeByNullCheck(type, expr, assumeTrue); } - if (expr.left.kind === SyntaxKind.TypeOfExpression && expr.right.kind === SyntaxKind.StringLiteral) { + if (expr.left.kind === SyntaxKind.TypeOfExpression && expr.right.kind === SyntaxKind.StringLiteral || + expr.left.kind === SyntaxKind.StringLiteral && expr.right.kind === SyntaxKind.TypeOfExpression) { return narrowTypeByTypeof(type, expr, assumeTrue); } break; @@ -7897,12 +7898,12 @@ namespace ts { function narrowTypeByTypeof(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { // We have '==', '!=', '====', or !==' operator with 'typeof xxx' on the left // and string literal on the right - const left = expr.left; - const right = expr.right; - if (!isMatchingReference(reference, left.expression)) { + const typeOf = (expr.left.kind === SyntaxKind.TypeOfExpression ? expr.left : expr.right); + const literal = (expr.right.kind === SyntaxKind.StringLiteral ? expr.right : expr.left); + if (!isMatchingReference(reference, typeOf.expression)) { // For a reference of the form 'x.y', a 'typeof x === ...' type guard resets the // narrowed type of 'y' to its declared type. - if (containsMatchingReference(reference, left.expression)) { + if (containsMatchingReference(reference, typeOf.expression)) { return declaredType; } return type; @@ -7915,14 +7916,14 @@ namespace ts { // We narrow a non-union type to an exact primitive type if the non-union type // is a supertype of that primtive type. For example, type 'any' can be narrowed // to one of the primitive types. - const targetType = getProperty(typeofTypesByName, right.text); + const targetType = getProperty(typeofTypesByName, literal.text); if (targetType && isTypeSubtypeOf(targetType, type)) { return targetType; } } const facts = assumeTrue ? - getProperty(typeofEQFacts, right.text) || TypeFacts.TypeofEQHostObject : - getProperty(typeofNEFacts, right.text) || TypeFacts.TypeofNEHostObject; + getProperty(typeofEQFacts, literal.text) || TypeFacts.TypeofEQHostObject : + getProperty(typeofNEFacts, literal.text) || TypeFacts.TypeofNEHostObject; return getTypeWithFacts(type, facts); }