diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 025eeecc30624..4915d0cff6679 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -33975,6 +33975,10 @@ namespace ts { case SyntaxKind.ExclamationEqualsToken: case SyntaxKind.EqualsEqualsEqualsToken: case SyntaxKind.ExclamationEqualsEqualsToken: + if (isLiteralExpressionOfObject(left) || isLiteralExpressionOfObject(right)) { + 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"); + } reportOperatorErrorUnless((left, right) => isTypeEqualityComparableTo(left, right) || isTypeEqualityComparableTo(right, left)); return booleanType; diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index fa6b9fd3efc55..5a8470eacf170 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3455,6 +3455,10 @@ "category": "Error", "code": 2838 }, + "This condition will always return '{0}' since JavaScript compares objects by reference, not value.": { + "category": "Error", + "code": 2839 + }, "Import declaration '{0}' is using private name '{1}'.": { "category": "Error", diff --git a/src/compiler/utilitiesPublic.ts b/src/compiler/utilitiesPublic.ts index 2f4ed3b14ccc1..4eca9fefba21b 100644 --- a/src/compiler/utilitiesPublic.ts +++ b/src/compiler/utilitiesPublic.ts @@ -1114,6 +1114,19 @@ namespace ts { return isLiteralKind(node.kind); } + /** @internal */ + export function isLiteralExpressionOfObject(node: Node) { + switch (node.kind) { + case SyntaxKind.ObjectLiteralExpression: + case SyntaxKind.ArrayLiteralExpression: + case SyntaxKind.RegularExpressionLiteral: + case SyntaxKind.FunctionExpression: + case SyntaxKind.ClassExpression: + return true; + } + return false; + } + // Pseudo-literals /* @internal */ diff --git a/tests/baselines/reference/conditionalEqualityOnLiteralObjects.errors.txt b/tests/baselines/reference/conditionalEqualityOnLiteralObjects.errors.txt new file mode 100644 index 0000000000000..f76436aa33877 --- /dev/null +++ b/tests/baselines/reference/conditionalEqualityOnLiteralObjects.errors.txt @@ -0,0 +1,130 @@ +tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(4,5): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value. +tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(6,5): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value. +tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(8,5): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value. +tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(10,5): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value. +tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(12,5): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value. +tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(14,5): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value. +tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(17,5): error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value. +tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(19,5): error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value. +tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(21,5): error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value. +tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(23,5): error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value. +tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(25,5): error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value. +tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(27,5): error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value. +tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(30,5): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value. +tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(32,5): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value. +tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(34,5): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value. +tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(36,5): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value. +tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(38,5): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value. +tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(40,5): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value. +tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(43,5): error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value. +tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(45,5): error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value. +tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(47,5): error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value. +tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(49,5): error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value. +tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(51,5): error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value. +tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(53,5): error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value. + + +==== tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts (24 errors) ==== + const a = { a: 1 } + const b = [1] + + if ({ a: 1 } === { a: 1 }) { + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value. + } + if ([1] === [1]) { + ~~~~~~~~~~~ +!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value. + } + if (a === { a: 1 }) { + ~~~~~~~~~~~~~~ +!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value. + } + if (b === [1]) { + ~~~~~~~~~ +!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value. + } + if ({ a: 1 } === a) { + ~~~~~~~~~~~~~~ +!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value. + } + if ([1] === b) { + ~~~~~~~~~ +!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value. + } + + if ({ a: 1 } !== { a: 1 }) { + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value. + } + if ([1] !== [1]) { + ~~~~~~~~~~~ +!!! error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value. + } + if (a !== { a: 1 }) { + ~~~~~~~~~~~~~~ +!!! error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value. + } + if (b !== [1]) { + ~~~~~~~~~ +!!! error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value. + } + if ({ a: 1 } !== a) { + ~~~~~~~~~~~~~~ +!!! error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value. + } + if ([1] !== b) { + ~~~~~~~~~ +!!! error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value. + } + + if ({ a: 1 } == { a: 1 }) { + ~~~~~~~~~~~~~~~~~~~~ +!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value. + } + if ([1] == [1]) { + ~~~~~~~~~~ +!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value. + } + if (a == { a: 1 }) { + ~~~~~~~~~~~~~ +!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value. + } + if (b == [1]) { + ~~~~~~~~ +!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value. + } + if ({ a: 1 } == a) { + ~~~~~~~~~~~~~ +!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value. + } + if ([1] == b) { + ~~~~~~~~ +!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value. + } + + if ({ a: 1 } != { a: 1 }) { + ~~~~~~~~~~~~~~~~~~~~ +!!! error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value. + } + if ([1] != [1]) { + ~~~~~~~~~~ +!!! error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value. + } + if (a != { a: 1 }) { + ~~~~~~~~~~~~~ +!!! error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value. + } + if (b != [1]) { + ~~~~~~~~ +!!! error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value. + } + if ({ a: 1 } != a) { + ~~~~~~~~~~~~~ +!!! error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value. + } + if ([1] != b) { + ~~~~~~~~ +!!! error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value. + } + \ No newline at end of file diff --git a/tests/baselines/reference/conditionalEqualityOnLiteralObjects.js b/tests/baselines/reference/conditionalEqualityOnLiteralObjects.js new file mode 100644 index 0000000000000..ab1ca51bb722e --- /dev/null +++ b/tests/baselines/reference/conditionalEqualityOnLiteralObjects.js @@ -0,0 +1,108 @@ +//// [conditionalEqualityOnLiteralObjects.ts] +const a = { a: 1 } +const b = [1] + +if ({ a: 1 } === { a: 1 }) { +} +if ([1] === [1]) { +} +if (a === { a: 1 }) { +} +if (b === [1]) { +} +if ({ a: 1 } === a) { +} +if ([1] === b) { +} + +if ({ a: 1 } !== { a: 1 }) { +} +if ([1] !== [1]) { +} +if (a !== { a: 1 }) { +} +if (b !== [1]) { +} +if ({ a: 1 } !== a) { +} +if ([1] !== b) { +} + +if ({ a: 1 } == { a: 1 }) { +} +if ([1] == [1]) { +} +if (a == { a: 1 }) { +} +if (b == [1]) { +} +if ({ a: 1 } == a) { +} +if ([1] == b) { +} + +if ({ a: 1 } != { a: 1 }) { +} +if ([1] != [1]) { +} +if (a != { a: 1 }) { +} +if (b != [1]) { +} +if ({ a: 1 } != a) { +} +if ([1] != b) { +} + + +//// [conditionalEqualityOnLiteralObjects.js] +var a = { a: 1 }; +var b = [1]; +if ({ a: 1 } === { a: 1 }) { +} +if ([1] === [1]) { +} +if (a === { a: 1 }) { +} +if (b === [1]) { +} +if ({ a: 1 } === a) { +} +if ([1] === b) { +} +if ({ a: 1 } !== { a: 1 }) { +} +if ([1] !== [1]) { +} +if (a !== { a: 1 }) { +} +if (b !== [1]) { +} +if ({ a: 1 } !== a) { +} +if ([1] !== b) { +} +if ({ a: 1 } == { a: 1 }) { +} +if ([1] == [1]) { +} +if (a == { a: 1 }) { +} +if (b == [1]) { +} +if ({ a: 1 } == a) { +} +if ([1] == b) { +} +if ({ a: 1 } != { a: 1 }) { +} +if ([1] != [1]) { +} +if (a != { a: 1 }) { +} +if (b != [1]) { +} +if ({ a: 1 } != a) { +} +if ([1] != b) { +} diff --git a/tests/baselines/reference/conditionalEqualityOnLiteralObjects.symbols b/tests/baselines/reference/conditionalEqualityOnLiteralObjects.symbols new file mode 100644 index 0000000000000..0be4f1e5c9748 --- /dev/null +++ b/tests/baselines/reference/conditionalEqualityOnLiteralObjects.symbols @@ -0,0 +1,92 @@ +=== tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts === +const a = { a: 1 } +>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 0, 5)) +>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 0, 11)) + +const b = [1] +>b : Symbol(b, Decl(conditionalEqualityOnLiteralObjects.ts, 1, 5)) + +if ({ a: 1 } === { a: 1 }) { +>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 3, 5)) +>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 3, 18)) +} +if ([1] === [1]) { +} +if (a === { a: 1 }) { +>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 0, 5)) +>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 7, 11)) +} +if (b === [1]) { +>b : Symbol(b, Decl(conditionalEqualityOnLiteralObjects.ts, 1, 5)) +} +if ({ a: 1 } === a) { +>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 11, 5)) +>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 0, 5)) +} +if ([1] === b) { +>b : Symbol(b, Decl(conditionalEqualityOnLiteralObjects.ts, 1, 5)) +} + +if ({ a: 1 } !== { a: 1 }) { +>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 16, 5)) +>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 16, 18)) +} +if ([1] !== [1]) { +} +if (a !== { a: 1 }) { +>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 0, 5)) +>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 20, 11)) +} +if (b !== [1]) { +>b : Symbol(b, Decl(conditionalEqualityOnLiteralObjects.ts, 1, 5)) +} +if ({ a: 1 } !== a) { +>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 24, 5)) +>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 0, 5)) +} +if ([1] !== b) { +>b : Symbol(b, Decl(conditionalEqualityOnLiteralObjects.ts, 1, 5)) +} + +if ({ a: 1 } == { a: 1 }) { +>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 29, 5)) +>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 29, 17)) +} +if ([1] == [1]) { +} +if (a == { a: 1 }) { +>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 0, 5)) +>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 33, 10)) +} +if (b == [1]) { +>b : Symbol(b, Decl(conditionalEqualityOnLiteralObjects.ts, 1, 5)) +} +if ({ a: 1 } == a) { +>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 37, 5)) +>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 0, 5)) +} +if ([1] == b) { +>b : Symbol(b, Decl(conditionalEqualityOnLiteralObjects.ts, 1, 5)) +} + +if ({ a: 1 } != { a: 1 }) { +>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 42, 5)) +>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 42, 17)) +} +if ([1] != [1]) { +} +if (a != { a: 1 }) { +>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 0, 5)) +>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 46, 10)) +} +if (b != [1]) { +>b : Symbol(b, Decl(conditionalEqualityOnLiteralObjects.ts, 1, 5)) +} +if ({ a: 1 } != a) { +>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 50, 5)) +>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 0, 5)) +} +if ([1] != b) { +>b : Symbol(b, Decl(conditionalEqualityOnLiteralObjects.ts, 1, 5)) +} + diff --git a/tests/baselines/reference/conditionalEqualityOnLiteralObjects.types b/tests/baselines/reference/conditionalEqualityOnLiteralObjects.types new file mode 100644 index 0000000000000..8287257837c16 --- /dev/null +++ b/tests/baselines/reference/conditionalEqualityOnLiteralObjects.types @@ -0,0 +1,184 @@ +=== tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts === +const a = { a: 1 } +>a : { a: number; } +>{ a: 1 } : { a: number; } +>a : number +>1 : 1 + +const b = [1] +>b : number[] +>[1] : number[] +>1 : 1 + +if ({ a: 1 } === { a: 1 }) { +>{ a: 1 } === { a: 1 } : boolean +>{ a: 1 } : { a: number; } +>a : number +>1 : 1 +>{ a: 1 } : { a: number; } +>a : number +>1 : 1 +} +if ([1] === [1]) { +>[1] === [1] : boolean +>[1] : number[] +>1 : 1 +>[1] : number[] +>1 : 1 +} +if (a === { a: 1 }) { +>a === { a: 1 } : boolean +>a : { a: number; } +>{ a: 1 } : { a: number; } +>a : number +>1 : 1 +} +if (b === [1]) { +>b === [1] : boolean +>b : number[] +>[1] : number[] +>1 : 1 +} +if ({ a: 1 } === a) { +>{ a: 1 } === a : boolean +>{ a: 1 } : { a: number; } +>a : number +>1 : 1 +>a : { a: number; } +} +if ([1] === b) { +>[1] === b : boolean +>[1] : number[] +>1 : 1 +>b : number[] +} + +if ({ a: 1 } !== { a: 1 }) { +>{ a: 1 } !== { a: 1 } : boolean +>{ a: 1 } : { a: number; } +>a : number +>1 : 1 +>{ a: 1 } : { a: number; } +>a : number +>1 : 1 +} +if ([1] !== [1]) { +>[1] !== [1] : boolean +>[1] : number[] +>1 : 1 +>[1] : number[] +>1 : 1 +} +if (a !== { a: 1 }) { +>a !== { a: 1 } : boolean +>a : { a: number; } +>{ a: 1 } : { a: number; } +>a : number +>1 : 1 +} +if (b !== [1]) { +>b !== [1] : boolean +>b : number[] +>[1] : number[] +>1 : 1 +} +if ({ a: 1 } !== a) { +>{ a: 1 } !== a : boolean +>{ a: 1 } : { a: number; } +>a : number +>1 : 1 +>a : { a: number; } +} +if ([1] !== b) { +>[1] !== b : boolean +>[1] : number[] +>1 : 1 +>b : number[] +} + +if ({ a: 1 } == { a: 1 }) { +>{ a: 1 } == { a: 1 } : boolean +>{ a: 1 } : { a: number; } +>a : number +>1 : 1 +>{ a: 1 } : { a: number; } +>a : number +>1 : 1 +} +if ([1] == [1]) { +>[1] == [1] : boolean +>[1] : number[] +>1 : 1 +>[1] : number[] +>1 : 1 +} +if (a == { a: 1 }) { +>a == { a: 1 } : boolean +>a : { a: number; } +>{ a: 1 } : { a: number; } +>a : number +>1 : 1 +} +if (b == [1]) { +>b == [1] : boolean +>b : number[] +>[1] : number[] +>1 : 1 +} +if ({ a: 1 } == a) { +>{ a: 1 } == a : boolean +>{ a: 1 } : { a: number; } +>a : number +>1 : 1 +>a : { a: number; } +} +if ([1] == b) { +>[1] == b : boolean +>[1] : number[] +>1 : 1 +>b : number[] +} + +if ({ a: 1 } != { a: 1 }) { +>{ a: 1 } != { a: 1 } : boolean +>{ a: 1 } : { a: number; } +>a : number +>1 : 1 +>{ a: 1 } : { a: number; } +>a : number +>1 : 1 +} +if ([1] != [1]) { +>[1] != [1] : boolean +>[1] : number[] +>1 : 1 +>[1] : number[] +>1 : 1 +} +if (a != { a: 1 }) { +>a != { a: 1 } : boolean +>a : { a: number; } +>{ a: 1 } : { a: number; } +>a : number +>1 : 1 +} +if (b != [1]) { +>b != [1] : boolean +>b : number[] +>[1] : number[] +>1 : 1 +} +if ({ a: 1 } != a) { +>{ a: 1 } != a : boolean +>{ a: 1 } : { a: number; } +>a : number +>1 : 1 +>a : { a: number; } +} +if ([1] != b) { +>[1] != b : boolean +>[1] : number[] +>1 : 1 +>b : number[] +} + diff --git a/tests/baselines/reference/functionImplementationErrors.errors.txt b/tests/baselines/reference/functionImplementationErrors.errors.txt index 5a9ebea09be94..cdd16bc902718 100644 --- a/tests/baselines/reference/functionImplementationErrors.errors.txt +++ b/tests/baselines/reference/functionImplementationErrors.errors.txt @@ -1,9 +1,10 @@ tests/cases/conformance/functions/functionImplementationErrors.ts(25,16): error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value. tests/cases/conformance/functions/functionImplementationErrors.ts(30,17): error TS2373: Parameter 'n' cannot reference identifier 'm' declared after it. tests/cases/conformance/functions/functionImplementationErrors.ts(35,17): error TS2373: Parameter 'n' cannot reference identifier 'm' declared after it. +tests/cases/conformance/functions/functionImplementationErrors.ts(40,1): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value. -==== tests/cases/conformance/functions/functionImplementationErrors.ts (3 errors) ==== +==== tests/cases/conformance/functions/functionImplementationErrors.ts (4 errors) ==== // FunctionExpression with no return type annotation with multiple return statements with unrelated types var f1 = function () { return ''; @@ -50,9 +51,14 @@ tests/cases/conformance/functions/functionImplementationErrors.ts(35,17): error // FunctionExpression with non -void return type annotation with a throw, no return, and other code // Should be error but isn't undefined === function (): number { + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ throw undefined; + ~~~~~~~~~~~~~~~~~~~~ var x = 4; + ~~~~~~~~~~~~~~ }; + ~ +!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value. class Base { private x; } class AnotherClass { private y; } diff --git a/tests/baselines/reference/functionImplementations.errors.txt b/tests/baselines/reference/functionImplementations.errors.txt new file mode 100644 index 0000000000000..e289bd7e40039 --- /dev/null +++ b/tests/baselines/reference/functionImplementations.errors.txt @@ -0,0 +1,164 @@ +tests/cases/conformance/functions/functionImplementations.ts(90,1): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value. + + +==== tests/cases/conformance/functions/functionImplementations.ts (1 errors) ==== + // FunctionExpression with no return type annotation and no return statement returns void + var v: void = function () { } (); + + // FunctionExpression f with no return type annotation and directly references f in its body returns any + var a: any = function f() { + return f; + }; + var a: any = function f() { + return f(); + }; + + // FunctionExpression f with no return type annotation and indirectly references f in its body returns any + var a: any = function f() { + var x = f; + return x; + }; + + // Two mutually recursive function implementations with no return type annotations + function rec1() { + return rec2(); + } + function rec2() { + return rec1(); + } + var a = rec1(); + var a = rec2(); + + // Two mutually recursive function implementations with return type annotation in one + function rec3(): number { + return rec4(); + } + function rec4() { + return rec3(); + } + var n: number; + var n = rec3(); + var n = rec4(); + + // FunctionExpression with no return type annotation and returns a number + var n = function () { + return 3; + } (); + + // FunctionExpression with no return type annotation and returns null + var nu = null; + var nu = function () { + return null; + } (); + + // FunctionExpression with no return type annotation and returns undefined + var un = undefined; + var un = function () { + return undefined; + } (); + + // FunctionExpression with no return type annotation and returns a type parameter type + var n = function (x: T) { + return x; + } (4); + + // FunctionExpression with no return type annotation and returns a constrained type parameter type + var n = function (x: T) { + return x; + } (4); + + // FunctionExpression with no return type annotation with multiple return statements with identical types + var n = function () { + return 3; + return 5; + }(); + + // Otherwise, the inferred return type is the first of the types of the return statement expressions + // in the function body that is a supertype of each of the others, + // ignoring return statements with no expressions. + // A compile - time error occurs if no return statement expression has a type that is a supertype of each of the others. + // FunctionExpression with no return type annotation with multiple return statements with subtype relation between returns + class Base { private m; } + class Derived extends Base { private q; } + var b: Base; + var b = function () { + return new Base(); return new Derived(); + } (); + + // FunctionExpression with no return type annotation with multiple return statements with one a recursive call + var a = function f() { + return new Base(); return new Derived(); return f(); // ? + } (); + + // FunctionExpression with non -void return type annotation with a single throw statement + undefined === function (): number { + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + throw undefined; + ~~~~~~~~~~~~~~~~~~~~ + }; + ~ +!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value. + + // Type of 'this' in function implementation is 'any' + function thisFunc() { + var x = this; + var x: any; + } + + // Function signature with optional parameter, no type annotation and initializer has initializer's type + function opt1(n = 4) { + var m = n; + var m: number; + } + + // Function signature with optional parameter, no type annotation and initializer has initializer's widened type + function opt2(n = { x: null, y: undefined }) { + var m = n; + var m: { x: any; y: any }; + } + + // Function signature with initializer referencing other parameter to the left + function opt3(n: number, m = n) { + var y = m; + var y: number; + } + + // Function signature with optional parameter has correct codegen + // (tested above) + + // FunctionExpression with non -void return type annotation return with no expression + function f6(): number { + return; + } + + class Derived2 extends Base { private r: string; } + class AnotherClass { private x } + // if f is a contextually typed function expression, the inferred return type is the union type + // of the types of the return statement expressions in the function body, + // ignoring return statements with no expressions. + var f7: (x: number) => string | number = x => { // should be (x: number) => number | string + if (x < 0) { return x; } + return x.toString(); + } + var f8: (x: number) => any = x => { // should be (x: number) => Base + return new Base(); + return new Derived2(); + } + var f9: (x: number) => any = x => { // should be (x: number) => Base + return new Base(); + return new Derived(); + return new Derived2(); + } + var f10: (x: number) => any = x => { // should be (x: number) => Derived | Derived1 + return new Derived(); + return new Derived2(); + } + var f11: (x: number) => any = x => { // should be (x: number) => Base | AnotherClass + return new Base(); + return new AnotherClass(); + } + var f12: (x: number) => any = x => { // should be (x: number) => Base | AnotherClass + return new Base(); + return; // should be ignored + return new AnotherClass(); + } \ No newline at end of file diff --git a/tests/baselines/reference/narrowByEquality.errors.txt b/tests/baselines/reference/narrowByEquality.errors.txt index 15610d6d1d49a..a484b58951576 100644 --- a/tests/baselines/reference/narrowByEquality.errors.txt +++ b/tests/baselines/reference/narrowByEquality.errors.txt @@ -1,10 +1,11 @@ +tests/cases/compiler/narrowByEquality.ts(41,5): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value. tests/cases/compiler/narrowByEquality.ts(54,15): error TS2322: Type 'string | number' is not assignable to type 'number'. Type 'string' is not assignable to type 'number'. tests/cases/compiler/narrowByEquality.ts(55,9): error TS2322: Type 'string | number' is not assignable to type 'number'. Type 'string' is not assignable to type 'number'. -==== tests/cases/compiler/narrowByEquality.ts (2 errors) ==== +==== tests/cases/compiler/narrowByEquality.ts (3 errors) ==== declare let x: number | string | boolean declare let n: number; declare let s: string; @@ -46,6 +47,8 @@ tests/cases/compiler/narrowByEquality.ts(55,9): error TS2322: Type 'string | num declare let xAndObj: number | string | boolean | object if (xAndObj == {}) { + ~~~~~~~~~~~~~ +!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value. xAndObj; } diff --git a/tests/baselines/reference/parserRealSource7.errors.txt b/tests/baselines/reference/parserRealSource7.errors.txt index 250b589873b37..a566f7212e3ff 100644 --- a/tests/baselines/reference/parserRealSource7.errors.txt +++ b/tests/baselines/reference/parserRealSource7.errors.txt @@ -265,6 +265,7 @@ tests/cases/conformance/parser/ecmascript5/parserRealSource7.ts(704,34): error T tests/cases/conformance/parser/ecmascript5/parserRealSource7.ts(710,69): error TS2304: Cannot find name 'NodeType'. tests/cases/conformance/parser/ecmascript5/parserRealSource7.ts(710,93): error TS2304: Cannot find name 'FuncDecl'. tests/cases/conformance/parser/ecmascript5/parserRealSource7.ts(710,136): error TS2304: Cannot find name 'FuncDecl'. +tests/cases/conformance/parser/ecmascript5/parserRealSource7.ts(722,17): error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value. tests/cases/conformance/parser/ecmascript5/parserRealSource7.ts(724,22): error TS2304: Cannot find name 'hasFlag'. tests/cases/conformance/parser/ecmascript5/parserRealSource7.ts(724,83): error TS2304: Cannot find name 'FncFlags'. tests/cases/conformance/parser/ecmascript5/parserRealSource7.ts(729,44): error TS2304: Cannot find name 'SymbolKind'. @@ -303,7 +304,7 @@ tests/cases/conformance/parser/ecmascript5/parserRealSource7.ts(827,34): error T tests/cases/conformance/parser/ecmascript5/parserRealSource7.ts(828,13): error TS2304: Cannot find name 'popTypeCollectionScope'. -==== tests/cases/conformance/parser/ecmascript5/parserRealSource7.ts (303 errors) ==== +==== tests/cases/conformance/parser/ecmascript5/parserRealSource7.ts (304 errors) ==== // Copyright (c) Microsoft. All rights reserved. Licensed under the Apache License, Version 2.0. // See LICENSE.txt in the project root for complete license information. @@ -1562,6 +1563,8 @@ tests/cases/conformance/parser/ecmascript5/parserRealSource7.ts(828,13): error T fgSym.type && fgSym.type.construct && fgSym.type.construct.signatures != [] && + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value. (fgSym.type.construct.signatures[0].declAST == null || !hasFlag(fgSym.type.construct.signatures[0].declAST.fncFlags, FncFlags.Ambient)) && ~~~~~~~ diff --git a/tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts b/tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts new file mode 100644 index 0000000000000..ef4a6f949c493 --- /dev/null +++ b/tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts @@ -0,0 +1,54 @@ +const a = { a: 1 } +const b = [1] + +if ({ a: 1 } === { a: 1 }) { +} +if ([1] === [1]) { +} +if (a === { a: 1 }) { +} +if (b === [1]) { +} +if ({ a: 1 } === a) { +} +if ([1] === b) { +} + +if ({ a: 1 } !== { a: 1 }) { +} +if ([1] !== [1]) { +} +if (a !== { a: 1 }) { +} +if (b !== [1]) { +} +if ({ a: 1 } !== a) { +} +if ([1] !== b) { +} + +if ({ a: 1 } == { a: 1 }) { +} +if ([1] == [1]) { +} +if (a == { a: 1 }) { +} +if (b == [1]) { +} +if ({ a: 1 } == a) { +} +if ([1] == b) { +} + +if ({ a: 1 } != { a: 1 }) { +} +if ([1] != [1]) { +} +if (a != { a: 1 }) { +} +if (b != [1]) { +} +if ({ a: 1 } != a) { +} +if ([1] != b) { +}