From 8db7af035d2406bed79cd29d3d7257fa8479f374 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 5 Mar 2016 11:16:31 -0800 Subject: [PATCH] Proper handling of 'null' and 'undefined' in equals and not equals guards --- src/compiler/checker.ts | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5cfdc6bc104..d90c8a740f4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6388,6 +6388,11 @@ namespace ts { return flags & TypeFlags.Nullable; } + function getNullableTypeOfKind(kind: TypeFlags) { + return kind & TypeFlags.Null ? kind & TypeFlags.Undefined ? + getUnionType([nullType, undefinedType]) : nullType : undefinedType; + } + function isNullableType(type: Type) { return getNullableKind(type) === TypeFlags.Nullable; } @@ -7216,12 +7221,11 @@ namespace ts { switch (expr.operatorToken.kind) { case SyntaxKind.EqualsEqualsToken: case SyntaxKind.ExclamationEqualsToken: + case SyntaxKind.EqualsEqualsEqualsToken: + case SyntaxKind.ExclamationEqualsEqualsToken: if (isNullOrUndefinedLiteral(expr.right)) { return narrowTypeByNullCheck(type, expr, assumeTrue); } - // Fall through - case SyntaxKind.EqualsEqualsEqualsToken: - case SyntaxKind.ExclamationEqualsEqualsToken: if (expr.left.kind === SyntaxKind.TypeOfExpression && expr.right.kind === SyntaxKind.StringLiteral) { return narrowTypeByTypeof(type, expr, assumeTrue); } @@ -7237,14 +7241,22 @@ namespace ts { } function narrowTypeByNullCheck(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { - // We have '==' or '!=' operator with 'null' or 'undefined' on the right - if (expr.operatorToken.kind === SyntaxKind.ExclamationEqualsToken) { + // We have '==', '!=', '===', or '!==' operator with 'null' or 'undefined' on the right + const operator = expr.operatorToken.kind; + if (operator === SyntaxKind.ExclamationEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) { assumeTrue = !assumeTrue; } - if (!strictNullChecks || assumeTrue || !isMatchingReference(expr.left, reference)) { + if (!strictNullChecks || !isMatchingReference(expr.left, reference)) { return type; } - return getNonNullableType(type); + const doubleEquals = operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsToken; + const exprNullableKind = doubleEquals ? TypeFlags.Nullable : + expr.right.kind === SyntaxKind.NullKeyword ? TypeFlags.Null : TypeFlags.Undefined; + if (assumeTrue) { + const nullableKind = getNullableKind(type) & exprNullableKind; + return nullableKind ? getNullableTypeOfKind(nullableKind) : type; + } + return removeNullableKind(type, exprNullableKind); } function narrowTypeByTypeof(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type {