diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 29101322aeb..c43b4ed17d2 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8732,7 +8732,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]); 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 440812664d8..559206ab098 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2464,6 +2464,10 @@ "category": "Message", "code": 6038 }, + "STRATEGY": { + "category": "Message", + "code": 6039 + }, "Compilation complete. Watching for file changes.": { "category": "Message", "code": 6042 @@ -2863,7 +2867,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 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..942b9f7055d --- /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" : "Expected \"pet\" to be a Pet" + } + 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