From 2bb2b092d465985053dff02871cb0bae533c7456 Mon Sep 17 00:00:00 2001 From: Christophe Vidal Date: Sat, 10 Sep 2016 09:37:51 +0700 Subject: [PATCH 1/4] Added a STRATEGY placeholder for the --moduleResolution option --- src/compiler/commandLineParser.ts | 1 + src/compiler/diagnosticMessages.json | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 9a958f68b73..742d57a12ff 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -294,6 +294,7 @@ namespace ts { "classic": ModuleResolutionKind.Classic, }), description: Diagnostics.Specify_module_resolution_strategy_Colon_node_Node_js_or_classic_TypeScript_pre_1_6, + paramType: Diagnostics.STRATEGY, }, { name: "allowUnusedLabels", diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index cddb132f56c..060eaa7bde3 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2472,6 +2472,10 @@ "category": "Message", "code": 6038 }, + "STRATEGY": { + "category": "Message", + "code": 6039 + }, "Compilation complete. Watching for file changes.": { "category": "Message", "code": 6042 @@ -2871,7 +2875,7 @@ "Element implicitly has an 'any' type because index expression is not of type 'number'.": { "category": "Error", "code": 7015 - }, + }, "Index signature of object type implicitly has an 'any' type.": { "category": "Error", "code": 7017 From 0f90d880b47381698dc66aa6ef97a372e5a2e0ee Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 10 Sep 2016 07:13:57 -0700 Subject: [PATCH 2/4] Preserve type parameter types in narrowing --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 79488b7b546..98a26fb0b33 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8682,7 +8682,7 @@ namespace ts { // type. Otherwise, the types are completely unrelated, so narrow to an intersection of the // two types. const targetType = type.flags & TypeFlags.TypeParameter ? getApparentType(type) : type; - return isTypeSubtypeOf(candidate, targetType) ? candidate : + return isTypeSubtypeOf(candidate, type) ? candidate : isTypeAssignableTo(type, candidate) ? type : isTypeAssignableTo(candidate, targetType) ? candidate : getIntersectionType([type, candidate]); From fbebbcaa7e663d6d2c8070190cc92d7b564f3237 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 10 Sep 2016 07:20:05 -0700 Subject: [PATCH 3/4] Add regression test --- .../narrowingConstrainedTypeParameter.js | 32 ++++++++++++ .../narrowingConstrainedTypeParameter.symbols | 42 +++++++++++++++ .../narrowingConstrainedTypeParameter.types | 52 +++++++++++++++++++ .../narrowingConstrainedTypeParameter.ts | 18 +++++++ 4 files changed, 144 insertions(+) create mode 100644 tests/baselines/reference/narrowingConstrainedTypeParameter.js create mode 100644 tests/baselines/reference/narrowingConstrainedTypeParameter.symbols create mode 100644 tests/baselines/reference/narrowingConstrainedTypeParameter.types create mode 100644 tests/cases/compiler/narrowingConstrainedTypeParameter.ts diff --git a/tests/baselines/reference/narrowingConstrainedTypeParameter.js b/tests/baselines/reference/narrowingConstrainedTypeParameter.js new file mode 100644 index 00000000000..3107585d941 --- /dev/null +++ b/tests/baselines/reference/narrowingConstrainedTypeParameter.js @@ -0,0 +1,32 @@ +//// [narrowingConstrainedTypeParameter.ts] + +// Repro from #10811 + +interface Pet { + name: string; +} + +function isPet(pet: any): pet is Pet { + return typeof pet.name === "string"; +} + +export function speak(pet: TPet, voice: (pet: TPet) => string): string { + if (!isPet(pet)) { + throw new Error("Expected \"pet\" to be a Pet"); + } + return voice(pet); +} + +//// [narrowingConstrainedTypeParameter.js] +// Repro from #10811 +"use strict"; +function isPet(pet) { + return typeof pet.name === "string"; +} +function speak(pet, voice) { + if (!isPet(pet)) { + throw new Error("Expected \"pet\" to be a Pet"); + } + return voice(pet); +} +exports.speak = speak; diff --git a/tests/baselines/reference/narrowingConstrainedTypeParameter.symbols b/tests/baselines/reference/narrowingConstrainedTypeParameter.symbols new file mode 100644 index 00000000000..89ca39e98a2 --- /dev/null +++ b/tests/baselines/reference/narrowingConstrainedTypeParameter.symbols @@ -0,0 +1,42 @@ +=== tests/cases/compiler/narrowingConstrainedTypeParameter.ts === + +// Repro from #10811 + +interface Pet { +>Pet : Symbol(Pet, Decl(narrowingConstrainedTypeParameter.ts, 0, 0)) + + name: string; +>name : Symbol(Pet.name, Decl(narrowingConstrainedTypeParameter.ts, 3, 15)) +} + +function isPet(pet: any): pet is Pet { +>isPet : Symbol(isPet, Decl(narrowingConstrainedTypeParameter.ts, 5, 1)) +>pet : Symbol(pet, Decl(narrowingConstrainedTypeParameter.ts, 7, 15)) +>pet : Symbol(pet, Decl(narrowingConstrainedTypeParameter.ts, 7, 15)) +>Pet : Symbol(Pet, Decl(narrowingConstrainedTypeParameter.ts, 0, 0)) + + return typeof pet.name === "string"; +>pet : Symbol(pet, Decl(narrowingConstrainedTypeParameter.ts, 7, 15)) +} + +export function speak(pet: TPet, voice: (pet: TPet) => string): string { +>speak : Symbol(speak, Decl(narrowingConstrainedTypeParameter.ts, 9, 1)) +>TPet : Symbol(TPet, Decl(narrowingConstrainedTypeParameter.ts, 11, 22)) +>Pet : Symbol(Pet, Decl(narrowingConstrainedTypeParameter.ts, 0, 0)) +>pet : Symbol(pet, Decl(narrowingConstrainedTypeParameter.ts, 11, 40)) +>TPet : Symbol(TPet, Decl(narrowingConstrainedTypeParameter.ts, 11, 22)) +>voice : Symbol(voice, Decl(narrowingConstrainedTypeParameter.ts, 11, 50)) +>pet : Symbol(pet, Decl(narrowingConstrainedTypeParameter.ts, 11, 59)) +>TPet : Symbol(TPet, Decl(narrowingConstrainedTypeParameter.ts, 11, 22)) + + if (!isPet(pet)) { +>isPet : Symbol(isPet, Decl(narrowingConstrainedTypeParameter.ts, 5, 1)) +>pet : Symbol(pet, Decl(narrowingConstrainedTypeParameter.ts, 11, 40)) + + throw new Error("Expected \"pet\" to be a Pet"); +>Error : Symbol(Error, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + } + return voice(pet); +>voice : Symbol(voice, Decl(narrowingConstrainedTypeParameter.ts, 11, 50)) +>pet : Symbol(pet, Decl(narrowingConstrainedTypeParameter.ts, 11, 40)) +} diff --git a/tests/baselines/reference/narrowingConstrainedTypeParameter.types b/tests/baselines/reference/narrowingConstrainedTypeParameter.types new file mode 100644 index 00000000000..adefb63ab88 --- /dev/null +++ b/tests/baselines/reference/narrowingConstrainedTypeParameter.types @@ -0,0 +1,52 @@ +=== tests/cases/compiler/narrowingConstrainedTypeParameter.ts === + +// Repro from #10811 + +interface Pet { +>Pet : Pet + + name: string; +>name : string +} + +function isPet(pet: any): pet is Pet { +>isPet : (pet: any) => pet is Pet +>pet : any +>pet : any +>Pet : Pet + + return typeof pet.name === "string"; +>typeof pet.name === "string" : boolean +>typeof pet.name : string +>pet.name : any +>pet : any +>name : any +>"string" : "string" +} + +export function speak(pet: TPet, voice: (pet: TPet) => string): string { +>speak : (pet: TPet, voice: (pet: TPet) => string) => string +>TPet : TPet +>Pet : Pet +>pet : TPet +>TPet : TPet +>voice : (pet: TPet) => string +>pet : TPet +>TPet : TPet + + if (!isPet(pet)) { +>!isPet(pet) : boolean +>isPet(pet) : boolean +>isPet : (pet: any) => pet is Pet +>pet : TPet + + throw new Error("Expected \"pet\" to be a Pet"); +>new Error("Expected \"pet\" to be a Pet") : Error +>Error : ErrorConstructor +>"Expected \"pet\" to be a Pet" : string + } + return voice(pet); +>voice(pet) : string +>voice : (pet: TPet) => string +>pet : TPet +} diff --git a/tests/cases/compiler/narrowingConstrainedTypeParameter.ts b/tests/cases/compiler/narrowingConstrainedTypeParameter.ts new file mode 100644 index 00000000000..8eacbca0119 --- /dev/null +++ b/tests/cases/compiler/narrowingConstrainedTypeParameter.ts @@ -0,0 +1,18 @@ +// @strictNullChecks: true + +// Repro from #10811 + +interface Pet { + name: string; +} + +function isPet(pet: any): pet is Pet { + return typeof pet.name === "string"; +} + +export function speak(pet: TPet, voice: (pet: TPet) => string): string { + if (!isPet(pet)) { + throw new Error("Expected \"pet\" to be a Pet"); + } + return voice(pet); +} \ No newline at end of file From b526aa3329ce133313ec0ac7acf5106a4cf99a79 Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Mon, 12 Sep 2016 14:06:55 -0700 Subject: [PATCH 4/4] Update failing test baseline --- .../baselines/reference/narrowingConstrainedTypeParameter.types | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/baselines/reference/narrowingConstrainedTypeParameter.types b/tests/baselines/reference/narrowingConstrainedTypeParameter.types index adefb63ab88..942b9f7055d 100644 --- a/tests/baselines/reference/narrowingConstrainedTypeParameter.types +++ b/tests/baselines/reference/narrowingConstrainedTypeParameter.types @@ -43,7 +43,7 @@ export function speak(pet: TPet, voice: (pet: TPet) => string) throw new Error("Expected \"pet\" to be a Pet"); >new Error("Expected \"pet\" to be a Pet") : Error >Error : ErrorConstructor ->"Expected \"pet\" to be a Pet" : string +>"Expected \"pet\" to be a Pet" : "Expected \"pet\" to be a Pet" } return voice(pet); >voice(pet) : string