From e52ed1a23a470db3465704b8bc3c36dcead1eb4e Mon Sep 17 00:00:00 2001 From: gcnew Date: Thu, 20 Jul 2017 02:48:30 +0300 Subject: [PATCH 1/2] Check the return type of type guard functions --- src/compiler/checker.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 70827becffe..253a8708f3a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -17996,6 +17996,8 @@ namespace ts { return; } + checkSourceElement(node.type); + const { parameterName } = node; if (isThisTypePredicate(typePredicate)) { getTypeFromThisTypeNode(parameterName as ThisTypeNode); From 0654fa285cc1f349f0ef2f2a61b4ee4fed27a038 Mon Sep 17 00:00:00 2001 From: gcnew Date: Thu, 20 Jul 2017 02:50:55 +0300 Subject: [PATCH 2/2] Added tests --- .../typeGuardFunctionErrors.errors.txt | 63 +++++++++++++++++-- .../reference/typeGuardFunctionErrors.js | 32 ++++++++-- .../typeGuards/typeGuardFunctionErrors.ts | 28 ++++++++- 3 files changed, 112 insertions(+), 11 deletions(-) diff --git a/tests/baselines/reference/typeGuardFunctionErrors.errors.txt b/tests/baselines/reference/typeGuardFunctionErrors.errors.txt index 63442a93b6b..46455687af2 100644 --- a/tests/baselines/reference/typeGuardFunctionErrors.errors.txt +++ b/tests/baselines/reference/typeGuardFunctionErrors.errors.txt @@ -62,9 +62,21 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(123,20 tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(128,34): error TS1230: A type predicate cannot reference element 'p1' in a binding pattern. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(132,34): error TS1230: A type predicate cannot reference element 'p1' in a binding pattern. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(136,39): error TS1230: A type predicate cannot reference element 'p1' in a binding pattern. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(152,68): error TS2344: Type 'T | "d"' does not satisfy the constraint 'Keys'. + Type '"d"' is not assignable to type 'Keys'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(159,31): error TS2344: Type 'Bar' does not satisfy the constraint 'Foo'. + Types of property ''a'' are incompatible. + Type 'number' is not assignable to type 'string'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(162,31): error TS2344: Type 'Bar' does not satisfy the constraint 'Foo'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(163,35): error TS2344: Type 'number' does not satisfy the constraint 'Foo'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(164,51): error TS2344: Type 'Bar' does not satisfy the constraint 'Foo'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(165,51): error TS2344: Type 'number' does not satisfy the constraint 'Foo'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(166,45): error TS2677: A type predicate's type must be assignable to its parameter's type. + Type 'NeedsFoo' is not assignable to type 'number'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(166,54): error TS2344: Type 'number' does not satisfy the constraint 'Foo'. -==== tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts (54 errors) ==== +==== tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts (62 errors) ==== class A { ~ !!! error TS2300: Duplicate identifier 'A'. @@ -175,7 +187,7 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(136,39 // No type guard in if statement if (hasNoTypeGuard(a)) { - a.propB; + a.propB; ~~~~~ !!! error TS2551: Property 'propB' does not exist on type 'A'. Did you mean 'propA'? } @@ -208,7 +220,7 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(136,39 return true; }; - // No matching signature + // No matching signature var assign3: (p1, p2) => p1 is A; assign3 = function(p1, p2, p3): p1 is A { ~~~~~~~ @@ -326,4 +338,47 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(136,39 var x: A; if (hasMissingParameter()) { x.propA; - } \ No newline at end of file + } + + // repro #17297 + + type Keys = 'a'|'b'|'c' + type KeySet = { [k in T]: true } + + // expected an error, since Keys doesn't have a 'd' + declare function hasKey(x: KeySet): x is KeySet; + ~~~~~ +!!! error TS2344: Type 'T | "d"' does not satisfy the constraint 'Keys'. +!!! error TS2344: Type '"d"' is not assignable to type 'Keys'. + + type Foo = { 'a': string; } + type Bar = { 'a': number; } + + interface NeedsFoo { + foo: T; + isFoo(): this is NeedsFoo; // should error + ~~~ +!!! error TS2344: Type 'Bar' does not satisfy the constraint 'Foo'. +!!! error TS2344: Types of property ''a'' are incompatible. +!!! error TS2344: Type 'number' is not assignable to type 'string'. + }; + + declare var anError: NeedsFoo; // error, as expected + ~~~ +!!! error TS2344: Type 'Bar' does not satisfy the constraint 'Foo'. + declare var alsoAnError: NeedsFoo; // also error, as expected + ~~~~~~ +!!! error TS2344: Type 'number' does not satisfy the constraint 'Foo'. + declare function newError1(x: any): x is NeedsFoo; // should error + ~~~ +!!! error TS2344: Type 'Bar' does not satisfy the constraint 'Foo'. + declare function newError2(x: any): x is NeedsFoo; // should error + ~~~~~~ +!!! error TS2344: Type 'number' does not satisfy the constraint 'Foo'. + declare function newError3(x: number): x is NeedsFoo; // should error + ~~~~~~~~~~~~~~~~ +!!! error TS2677: A type predicate's type must be assignable to its parameter's type. +!!! error TS2677: Type 'NeedsFoo' is not assignable to type 'number'. + ~~~~~~ +!!! error TS2344: Type 'number' does not satisfy the constraint 'Foo'. + \ No newline at end of file diff --git a/tests/baselines/reference/typeGuardFunctionErrors.js b/tests/baselines/reference/typeGuardFunctionErrors.js index cc773ae11b1..12ffab852d8 100644 --- a/tests/baselines/reference/typeGuardFunctionErrors.js +++ b/tests/baselines/reference/typeGuardFunctionErrors.js @@ -67,7 +67,7 @@ if (funA(0, a)) { // No type guard in if statement if (hasNoTypeGuard(a)) { - a.propB; + a.propB; } // Type predicate type is not assignable @@ -86,7 +86,7 @@ assign2 = function(p1, p2): p2 is A { return true; }; -// No matching signature +// No matching signature var assign3: (p1, p2) => p1 is A; assign3 = function(p1, p2, p3): p1 is A { return true; @@ -142,7 +142,30 @@ function b7({a, b, c: {p1}}, p2, p3): p1 is A { var x: A; if (hasMissingParameter()) { x.propA; -} +} + +// repro #17297 + +type Keys = 'a'|'b'|'c' +type KeySet = { [k in T]: true } + +// expected an error, since Keys doesn't have a 'd' +declare function hasKey(x: KeySet): x is KeySet; + +type Foo = { 'a': string; } +type Bar = { 'a': number; } + +interface NeedsFoo { + foo: T; + isFoo(): this is NeedsFoo; // should error +}; + +declare var anError: NeedsFoo; // error, as expected +declare var alsoAnError: NeedsFoo; // also error, as expected +declare function newError1(x: any): x is NeedsFoo; // should error +declare function newError2(x: any): x is NeedsFoo; // should error +declare function newError3(x: number): x is NeedsFoo; // should error + //// [typeGuardFunctionErrors.js] var __extends = (this && this.__extends) || (function () { @@ -224,7 +247,7 @@ var assign2; assign2 = function (p1, p2) { return true; }; -// No matching signature +// No matching signature var assign3; assign3 = function (p1, p2, p3) { return true; @@ -290,3 +313,4 @@ var x; if (hasMissingParameter()) { x.propA; } +; diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts index 688099280c5..eb61473c5cc 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts @@ -67,7 +67,7 @@ if (funA(0, a)) { // No type guard in if statement if (hasNoTypeGuard(a)) { - a.propB; + a.propB; } // Type predicate type is not assignable @@ -86,7 +86,7 @@ assign2 = function(p1, p2): p2 is A { return true; }; -// No matching signature +// No matching signature var assign3: (p1, p2) => p1 is A; assign3 = function(p1, p2, p3): p1 is A { return true; @@ -142,4 +142,26 @@ function b7({a, b, c: {p1}}, p2, p3): p1 is A { var x: A; if (hasMissingParameter()) { x.propA; -} \ No newline at end of file +} + +// repro #17297 + +type Keys = 'a'|'b'|'c' +type KeySet = { [k in T]: true } + +// expected an error, since Keys doesn't have a 'd' +declare function hasKey(x: KeySet): x is KeySet; + +type Foo = { 'a': string; } +type Bar = { 'a': number; } + +interface NeedsFoo { + foo: T; + isFoo(): this is NeedsFoo; // should error +}; + +declare var anError: NeedsFoo; // error, as expected +declare var alsoAnError: NeedsFoo; // also error, as expected +declare function newError1(x: any): x is NeedsFoo; // should error +declare function newError2(x: any): x is NeedsFoo; // should error +declare function newError3(x: number): x is NeedsFoo; // should error