From 843aa6c1effe8365bb461a4a953d55eeb5dfa7cf Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 7 Jun 2016 07:51:34 -0700 Subject: [PATCH] Allow case comparison to undefined and null in strict null checking mode --- src/compiler/checker.ts | 2 +- ...llyTypedObjectLiteralReturnType.errors.txt | 18 ++++++++++++++++ ...ontextuallyTypedObjectLiteralReturnType.js | 21 +++++++++++++++++++ .../reference/equalityStrictNulls.errors.txt | 10 +++++++++ .../reference/equalityStrictNulls.js | 20 ++++++++++++++++++ tests/cases/compiler/inferFromConstraints.ts | 9 ++++++++ ...ontextuallyTypedObjectLiteralReturnType.ts | 9 ++++++++ .../comparable/equalityStrictNulls.ts | 10 +++++++++ 8 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/contextuallyTypedObjectLiteralReturnType.errors.txt create mode 100644 tests/baselines/reference/contextuallyTypedObjectLiteralReturnType.js create mode 100644 tests/cases/compiler/inferFromConstraints.ts create mode 100644 tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedObjectLiteralReturnType.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index be21090d780..60acefb6045 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -15106,7 +15106,7 @@ namespace ts { // In a 'switch' statement, each 'case' expression must be of a type that is comparable // to or from the type of the 'switch' expression. const caseType = checkExpression(caseClause.expression); - if (!isTypeComparableTo(expressionType, caseType)) { + if (!isTypeEqualityComparableTo(expressionType, caseType)) { // expressionType is not comparable to caseType, try the reversed check and report errors if it fails checkTypeComparableTo(caseType, expressionType, caseClause.expression, /*headMessage*/ undefined); } diff --git a/tests/baselines/reference/contextuallyTypedObjectLiteralReturnType.errors.txt b/tests/baselines/reference/contextuallyTypedObjectLiteralReturnType.errors.txt new file mode 100644 index 00000000000..b010de0f761 --- /dev/null +++ b/tests/baselines/reference/contextuallyTypedObjectLiteralReturnType.errors.txt @@ -0,0 +1,18 @@ +tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedObjectLiteralReturnType.ts(6,24): error TS7024: Function implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. +tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedObjectLiteralReturnType.ts(7,38): error TS2339: Property 'length' does not exist on type 'number'. + + +==== tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedObjectLiteralReturnType.ts (2 errors) ==== + function id(x: T): T { return x }; + // Correct: type of fnWrapper is (y: number) => { y: number } + var fn = function(y: number) { return { y } }; + var fnWrapper = id(fn); + // Incorrect: type of inlineWrapper is (z: number) => any + var inlineWrapper = id(function(z: number) { return { z } }); + ~~~~~~~~ +!!! error TS7024: Function implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. + let error1: number = fnWrapper(12).y.length; // should error + ~~~~~~ +!!! error TS2339: Property 'length' does not exist on type 'number'. + let error2: number = inlineWrapper(12).z.length; // should error + \ No newline at end of file diff --git a/tests/baselines/reference/contextuallyTypedObjectLiteralReturnType.js b/tests/baselines/reference/contextuallyTypedObjectLiteralReturnType.js new file mode 100644 index 00000000000..b687eaab5f5 --- /dev/null +++ b/tests/baselines/reference/contextuallyTypedObjectLiteralReturnType.js @@ -0,0 +1,21 @@ +//// [contextuallyTypedObjectLiteralReturnType.ts] +function id(x: T): T { return x }; +// Correct: type of fnWrapper is (y: number) => { y: number } +var fn = function(y: number) { return { y } }; +var fnWrapper = id(fn); +// Incorrect: type of inlineWrapper is (z: number) => any +var inlineWrapper = id(function(z: number) { return { z } }); +let error1: number = fnWrapper(12).y.length; // should error +let error2: number = inlineWrapper(12).z.length; // should error + + +//// [contextuallyTypedObjectLiteralReturnType.js] +function id(x) { return x; } +; +// Correct: type of fnWrapper is (y: number) => { y: number } +var fn = function (y) { return { y: y }; }; +var fnWrapper = id(fn); +// Incorrect: type of inlineWrapper is (z: number) => any +var inlineWrapper = id(function (z) { return { z: z }; }); +var error1 = fnWrapper(12).y.length; // should error +var error2 = inlineWrapper(12).z.length; // should error diff --git a/tests/baselines/reference/equalityStrictNulls.errors.txt b/tests/baselines/reference/equalityStrictNulls.errors.txt index 54b581c87e6..e0793542b84 100644 --- a/tests/baselines/reference/equalityStrictNulls.errors.txt +++ b/tests/baselines/reference/equalityStrictNulls.errors.txt @@ -81,4 +81,14 @@ tests/cases/conformance/types/typeRelationships/comparable/equalityStrictNulls.t !!! error TS2365: Operator '<=' cannot be applied to types 'number' and 'undefined'. } } + function f5(x: string) { + switch(x) { + case null: + break; + case undefined: + break; + default: + return; + } + } \ No newline at end of file diff --git a/tests/baselines/reference/equalityStrictNulls.js b/tests/baselines/reference/equalityStrictNulls.js index 99ff801a52c..e34e9fe9989 100644 --- a/tests/baselines/reference/equalityStrictNulls.js +++ b/tests/baselines/reference/equalityStrictNulls.js @@ -67,6 +67,16 @@ function f4(x: number) { if (x <= undefined) { } } +function f5(x: string) { + switch(x) { + case null: + break; + case undefined: + break; + default: + return; + } +} //// [equalityStrictNulls.js] @@ -134,3 +144,13 @@ function f4(x) { if (x <= undefined) { } } +function f5(x) { + switch (x) { + case null: + break; + case undefined: + break; + default: + return; + } +} diff --git a/tests/cases/compiler/inferFromConstraints.ts b/tests/cases/compiler/inferFromConstraints.ts new file mode 100644 index 00000000000..d41d5ce7c46 --- /dev/null +++ b/tests/cases/compiler/inferFromConstraints.ts @@ -0,0 +1,9 @@ +interface Ctor { + new(): T; +} +// declare function create1(ctor: Ctor): T; +declare function create2>(ctor: C): T; + +class A { a: number } +// let a1 = create1(A).a; // a: A --> OK +let a2 = create2(A).a; // a: {} --> Should be A diff --git a/tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedObjectLiteralReturnType.ts b/tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedObjectLiteralReturnType.ts new file mode 100644 index 00000000000..5e4500d1cb9 --- /dev/null +++ b/tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedObjectLiteralReturnType.ts @@ -0,0 +1,9 @@ +// @noImplicitAny: true +function id(x: T): T { return x }; +// Correct: type of fnWrapper is (y: number) => { y: number } +var fn = function(y: number) { return { y } }; +var fnWrapper = id(fn); +// Incorrect: type of inlineWrapper is (z: number) => any +var inlineWrapper = id(function(z: number) { return { z } }); +let error1: number = fnWrapper(12).y.length; // should error +let error2: number = inlineWrapper(12).z.length; // should error diff --git a/tests/cases/conformance/types/typeRelationships/comparable/equalityStrictNulls.ts b/tests/cases/conformance/types/typeRelationships/comparable/equalityStrictNulls.ts index 6b2744e8849..da46ab37a2a 100644 --- a/tests/cases/conformance/types/typeRelationships/comparable/equalityStrictNulls.ts +++ b/tests/cases/conformance/types/typeRelationships/comparable/equalityStrictNulls.ts @@ -67,3 +67,13 @@ function f4(x: number) { if (x <= undefined) { } } +function f5(x: string) { + switch(x) { + case null: + break; + case undefined: + break; + default: + return; + } +}