diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d1d09f32dd8cd..cf519cb869a7e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -40006,6 +40006,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const eqType = operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.EqualsEqualsEqualsToken; error(errorNode, Diagnostics.This_condition_will_always_return_0_since_JavaScript_compares_objects_by_reference_not_value, eqType ? "false" : "true"); } + const otherUndefinedOp = + (left.kind === SyntaxKind.Identifier && getSymbolAtLocation(left) === undefinedSymbol) ? right : + (right.kind === SyntaxKind.Identifier && getSymbolAtLocation(right) === undefinedSymbol) ? left : undefined; + if (otherUndefinedOp) { + if (getSyntacticNullishnessSemantics(otherUndefinedOp) !== PredicateSemantics.Sometimes) { + error(otherUndefinedOp, Diagnostics.This_binary_expression_is_never_nullish_Are_you_missing_parentheses); + } + } checkNaNEquality(errorNode, operator, left, right); reportOperatorErrorUnless((left, right) => isTypeEqualityComparableTo(left, right) || isTypeEqualityComparableTo(right, left)); } diff --git a/tests/baselines/reference/comparisonOperatorWithIdenticalPrimitiveType.errors.txt b/tests/baselines/reference/comparisonOperatorWithIdenticalPrimitiveType.errors.txt index b35444962ebcd..9b57089c3aa64 100644 --- a/tests/baselines/reference/comparisonOperatorWithIdenticalPrimitiveType.errors.txt +++ b/tests/baselines/reference/comparisonOperatorWithIdenticalPrimitiveType.errors.txt @@ -14,9 +14,13 @@ comparisonOperatorWithIdenticalPrimitiveType.ts(42,11): error TS18050: The value comparisonOperatorWithIdenticalPrimitiveType.ts(42,19): error TS18050: The value 'null' cannot be used here. comparisonOperatorWithIdenticalPrimitiveType.ts(43,11): error TS18050: The value 'undefined' cannot be used here. comparisonOperatorWithIdenticalPrimitiveType.ts(43,24): error TS18050: The value 'undefined' cannot be used here. +comparisonOperatorWithIdenticalPrimitiveType.ts(52,24): error TS2870: This binary expression is never nullish. Are you missing parentheses? +comparisonOperatorWithIdenticalPrimitiveType.ts(61,24): error TS2870: This binary expression is never nullish. Are you missing parentheses? +comparisonOperatorWithIdenticalPrimitiveType.ts(70,25): error TS2870: This binary expression is never nullish. Are you missing parentheses? +comparisonOperatorWithIdenticalPrimitiveType.ts(79,25): error TS2870: This binary expression is never nullish. Are you missing parentheses? -==== comparisonOperatorWithIdenticalPrimitiveType.ts (16 errors) ==== +==== comparisonOperatorWithIdenticalPrimitiveType.ts (20 errors) ==== enum E { a, b, c } var a: number; @@ -101,6 +105,8 @@ comparisonOperatorWithIdenticalPrimitiveType.ts(43,24): error TS18050: The value var re5 = e == e; var re6 = null == null; var re7 = undefined == undefined; + ~~~~~~~~~ +!!! error TS2870: This binary expression is never nullish. Are you missing parentheses? // operator != var rf1 = a != a; @@ -110,6 +116,8 @@ comparisonOperatorWithIdenticalPrimitiveType.ts(43,24): error TS18050: The value var rf5 = e != e; var rf6 = null != null; var rf7 = undefined != undefined; + ~~~~~~~~~ +!!! error TS2870: This binary expression is never nullish. Are you missing parentheses? // operator === var rg1 = a === a; @@ -119,6 +127,8 @@ comparisonOperatorWithIdenticalPrimitiveType.ts(43,24): error TS18050: The value var rg5 = e === e; var rg6 = null === null; var rg7 = undefined === undefined; + ~~~~~~~~~ +!!! error TS2870: This binary expression is never nullish. Are you missing parentheses? // operator !== var rh1 = a !== a; @@ -127,4 +137,6 @@ comparisonOperatorWithIdenticalPrimitiveType.ts(43,24): error TS18050: The value var rh4 = d !== d; var rh5 = e !== e; var rh6 = null !== null; - var rh7 = undefined !== undefined; \ No newline at end of file + var rh7 = undefined !== undefined; + ~~~~~~~~~ +!!! error TS2870: This binary expression is never nullish. Are you missing parentheses? \ No newline at end of file diff --git a/tests/baselines/reference/conditionalOperatorConditionIsBooleanType.errors.txt b/tests/baselines/reference/conditionalOperatorConditionIsBooleanType.errors.txt new file mode 100644 index 0000000000000..44640fc6f1e4e --- /dev/null +++ b/tests/baselines/reference/conditionalOperatorConditionIsBooleanType.errors.txt @@ -0,0 +1,74 @@ +conditionalOperatorConditionIsBooleanType.ts(36,1): error TS2870: This binary expression is never nullish. Are you missing parentheses? +conditionalOperatorConditionIsBooleanType.ts(38,1): error TS2870: This binary expression is never nullish. Are you missing parentheses? +conditionalOperatorConditionIsBooleanType.ts(59,23): error TS2870: This binary expression is never nullish. Are you missing parentheses? + + +==== conditionalOperatorConditionIsBooleanType.ts (3 errors) ==== + //Cond ? Expr1 : Expr2, Cond is of boolean type, Expr1 and Expr2 have the same type + var condBoolean: boolean; + + var exprAny1: any; + var exprBoolean1: boolean; + var exprNumber1: number; + var exprString1: string; + var exprIsObject1: Object; + + var exprAny2: any; + var exprBoolean2: boolean; + var exprNumber2: number; + var exprString2: string; + var exprIsObject2: Object; + + //Cond is a boolean type variable + condBoolean ? exprAny1 : exprAny2; + condBoolean ? exprBoolean1 : exprBoolean2; + condBoolean ? exprNumber1 : exprNumber2; + condBoolean ? exprString1 : exprString2; + condBoolean ? exprIsObject1 : exprIsObject2; + condBoolean ? exprString1 : exprBoolean1; // union + + //Cond is a boolean type literal + true ? exprAny1 : exprAny2; + false ? exprBoolean1 : exprBoolean2; + true ? exprNumber1 : exprNumber2; + false ? exprString1 : exprString2; + true ? exprIsObject1 : exprIsObject2; + true ? exprString1 : exprBoolean1; // union + + //Cond is a boolean type expression + !true ? exprAny1 : exprAny2; + typeof "123" == "string" ? exprBoolean1 : exprBoolean2; + 2 > 1 ? exprNumber1 : exprNumber2; + null === undefined ? exprString1 : exprString2; + ~~~~ +!!! error TS2870: This binary expression is never nullish. Are you missing parentheses? + true || false ? exprIsObject1 : exprIsObject2; + null === undefined ? exprString1 : exprBoolean1; // union + ~~~~ +!!! error TS2870: This binary expression is never nullish. Are you missing parentheses? + + //Results shoud be same as Expr1 and Expr2 + var resultIsAny1 = condBoolean ? exprAny1 : exprAny2; + var resultIsBoolean1 = condBoolean ? exprBoolean1 : exprBoolean2; + var resultIsNumber1 = condBoolean ? exprNumber1 : exprNumber2; + var resultIsString1 = condBoolean ? exprString1 : exprString2; + var resultIsObject1 = condBoolean ? exprIsObject1 : exprIsObject2; + var resultIsStringOrBoolean1 = condBoolean ? exprString1 : exprBoolean1; // union + + var resultIsAny2 = true ? exprAny1 : exprAny2; + var resultIsBoolean2 = false ? exprBoolean1 : exprBoolean2; + var resultIsNumber2 = true ? exprNumber1 : exprNumber2; + var resultIsString2 = false ? exprString1 : exprString2; + var resultIsObject2 = true ? exprIsObject1 : exprIsObject2; + var resultIsStringOrBoolean2 = true ? exprString1 : exprBoolean1; // union + var resultIsStringOrBoolean3 = false ? exprString1 : exprBoolean1; // union + + var resultIsAny3 = !true ? exprAny1 : exprAny2; + var resultIsBoolean3 = typeof "123" == "string" ? exprBoolean1 : exprBoolean2; + var resultIsNumber3 = 2 > 1 ? exprNumber1 : exprNumber2; + var resultIsString3 = null === undefined ? exprString1 : exprString2; + ~~~~ +!!! error TS2870: This binary expression is never nullish. Are you missing parentheses? + var resultIsObject3 = true || false ? exprIsObject1 : exprIsObject2; + var resultIsStringOrBoolean4 = typeof "123" === "string" ? exprString1 : exprBoolean1; // union + \ No newline at end of file diff --git a/tests/baselines/reference/conditionalOperatorConditionIsBooleanType.types b/tests/baselines/reference/conditionalOperatorConditionIsBooleanType.types index 627e36adfe15a..bf18d8723010c 100644 --- a/tests/baselines/reference/conditionalOperatorConditionIsBooleanType.types +++ b/tests/baselines/reference/conditionalOperatorConditionIsBooleanType.types @@ -8,6 +8,7 @@ var condBoolean: boolean; var exprAny1: any; >exprAny1 : any +> : ^^^ var exprBoolean1: boolean; >exprBoolean1 : boolean @@ -27,6 +28,7 @@ var exprIsObject1: Object; var exprAny2: any; >exprAny2 : any +> : ^^^ var exprBoolean2: boolean; >exprBoolean2 : boolean @@ -47,10 +49,13 @@ var exprIsObject2: Object; //Cond is a boolean type variable condBoolean ? exprAny1 : exprAny2; >condBoolean ? exprAny1 : exprAny2 : any +> : ^^^ >condBoolean : boolean > : ^^^^^^^ >exprAny1 : any +> : ^^^ >exprAny2 : any +> : ^^^ condBoolean ? exprBoolean1 : exprBoolean2; >condBoolean ? exprBoolean1 : exprBoolean2 : boolean @@ -105,10 +110,13 @@ condBoolean ? exprString1 : exprBoolean1; // union //Cond is a boolean type literal true ? exprAny1 : exprAny2; >true ? exprAny1 : exprAny2 : any +> : ^^^ >true : true > : ^^^^ >exprAny1 : any +> : ^^^ >exprAny2 : any +> : ^^^ false ? exprBoolean1 : exprBoolean2; >false ? exprBoolean1 : exprBoolean2 : boolean @@ -163,12 +171,15 @@ true ? exprString1 : exprBoolean1; // union //Cond is a boolean type expression !true ? exprAny1 : exprAny2; >!true ? exprAny1 : exprAny2 : any +> : ^^^ >!true : boolean > : ^^^^^^^ >true : true > : ^^^^ >exprAny1 : any +> : ^^^ >exprAny2 : any +> : ^^^ typeof "123" == "string" ? exprBoolean1 : exprBoolean2; >typeof "123" == "string" ? exprBoolean1 : exprBoolean2 : boolean @@ -241,11 +252,15 @@ null === undefined ? exprString1 : exprBoolean1; // union //Results shoud be same as Expr1 and Expr2 var resultIsAny1 = condBoolean ? exprAny1 : exprAny2; >resultIsAny1 : any +> : ^^^ >condBoolean ? exprAny1 : exprAny2 : any +> : ^^^ >condBoolean : boolean > : ^^^^^^^ >exprAny1 : any +> : ^^^ >exprAny2 : any +> : ^^^ var resultIsBoolean1 = condBoolean ? exprBoolean1 : exprBoolean2; >resultIsBoolean1 : boolean @@ -309,11 +324,15 @@ var resultIsStringOrBoolean1 = condBoolean ? exprString1 : exprBoolean1; // unio var resultIsAny2 = true ? exprAny1 : exprAny2; >resultIsAny2 : any +> : ^^^ >true ? exprAny1 : exprAny2 : any +> : ^^^ >true : true > : ^^^^ >exprAny1 : any +> : ^^^ >exprAny2 : any +> : ^^^ var resultIsBoolean2 = false ? exprBoolean1 : exprBoolean2; >resultIsBoolean2 : boolean @@ -389,13 +408,17 @@ var resultIsStringOrBoolean3 = false ? exprString1 : exprBoolean1; // union var resultIsAny3 = !true ? exprAny1 : exprAny2; >resultIsAny3 : any +> : ^^^ >!true ? exprAny1 : exprAny2 : any +> : ^^^ >!true : boolean > : ^^^^^^^ >true : true > : ^^^^ >exprAny1 : any +> : ^^^ >exprAny2 : any +> : ^^^ var resultIsBoolean3 = typeof "123" == "string" ? exprBoolean1 : exprBoolean2; >resultIsBoolean3 : boolean diff --git a/tests/baselines/reference/equalityStrictNulls.errors.txt b/tests/baselines/reference/equalityStrictNulls.errors.txt index cf5df03539841..97a31ceb4c87a 100644 --- a/tests/baselines/reference/equalityStrictNulls.errors.txt +++ b/tests/baselines/reference/equalityStrictNulls.errors.txt @@ -1,10 +1,13 @@ +equalityStrictNulls.ts(37,22): error TS2870: This binary expression is never nullish. Are you missing parentheses? +equalityStrictNulls.ts(39,22): error TS2870: This binary expression is never nullish. Are you missing parentheses? +equalityStrictNulls.ts(41,9): error TS2870: This binary expression is never nullish. Are you missing parentheses? equalityStrictNulls.ts(59,13): error TS18050: The value 'undefined' cannot be used here. equalityStrictNulls.ts(61,13): error TS18050: The value 'undefined' cannot be used here. equalityStrictNulls.ts(63,14): error TS18050: The value 'undefined' cannot be used here. equalityStrictNulls.ts(65,14): error TS18050: The value 'undefined' cannot be used here. -==== equalityStrictNulls.ts (4 errors) ==== +==== equalityStrictNulls.ts (7 errors) ==== function f1(x: string) { if (x == undefined) { } @@ -42,10 +45,16 @@ equalityStrictNulls.ts(65,14): error TS18050: The value 'undefined' cannot be us function f2() { if (undefined == undefined) { + ~~~~~~~~~ +!!! error TS2870: This binary expression is never nullish. Are you missing parentheses? } if (undefined == null) { + ~~~~ +!!! error TS2870: This binary expression is never nullish. Are you missing parentheses? } if (null == undefined) { + ~~~~ +!!! error TS2870: This binary expression is never nullish. Are you missing parentheses? } if (null == null) { } diff --git a/tests/baselines/reference/functionImplementationErrors.errors.txt b/tests/baselines/reference/functionImplementationErrors.errors.txt index 09a5987d82ae2..92fda40f0911c 100644 --- a/tests/baselines/reference/functionImplementationErrors.errors.txt +++ b/tests/baselines/reference/functionImplementationErrors.errors.txt @@ -2,9 +2,10 @@ functionImplementationErrors.ts(25,16): error TS2355: A function whose declared functionImplementationErrors.ts(30,17): error TS2373: Parameter 'n' cannot reference identifier 'm' declared after it. functionImplementationErrors.ts(35,17): error TS2373: Parameter 'n' cannot reference identifier 'm' declared after it. functionImplementationErrors.ts(40,1): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value. +functionImplementationErrors.ts(40,15): error TS2870: This binary expression is never nullish. Are you missing parentheses? -==== functionImplementationErrors.ts (4 errors) ==== +==== functionImplementationErrors.ts (5 errors) ==== // FunctionExpression with no return type annotation with multiple return statements with unrelated types var f1 = function () { return ''; @@ -52,6 +53,8 @@ functionImplementationErrors.ts(40,1): error TS2839: This condition will always // Should be error but isn't undefined === function (): number { ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~~~~ +!!! error TS2870: This binary expression is never nullish. Are you missing parentheses? throw undefined; ~~~~~~~~~~~~~~~~~~~~ var x = 4; diff --git a/tests/baselines/reference/functionImplementations.errors.txt b/tests/baselines/reference/functionImplementations.errors.txt index 316f06cdf5924..cac6ca8a94308 100644 --- a/tests/baselines/reference/functionImplementations.errors.txt +++ b/tests/baselines/reference/functionImplementations.errors.txt @@ -1,8 +1,9 @@ functionImplementations.ts(85,5): error TS2403: Subsequent variable declarations must have the same type. Variable 'a' must be of type 'any', but here has type 'Base'. functionImplementations.ts(90,1): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value. +functionImplementations.ts(90,15): error TS2870: This binary expression is never nullish. Are you missing parentheses? -==== functionImplementations.ts (2 errors) ==== +==== functionImplementations.ts (3 errors) ==== // FunctionExpression with no return type annotation and no return statement returns void var v: void = function () { } (); @@ -97,6 +98,8 @@ functionImplementations.ts(90,1): error TS2839: This condition will always retur // FunctionExpression with non -void return type annotation with a single throw statement undefined === function (): number { ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~~~~ +!!! error TS2870: This binary expression is never nullish. Are you missing parentheses? throw undefined; ~~~~~~~~~~~~~~~~~~~~ };