Merge pull request #10839 from Microsoft/fixNarrowingWithTypeParameter
Fix narrowing with constrained type parameter
This commit is contained in:
commit
1fce34be71
|
@ -8729,7 +8729,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]);
|
||||
|
|
|
@ -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<TPet extends Pet>(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;
|
|
@ -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<TPet extends Pet>(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))
|
||||
}
|
|
@ -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<TPet extends Pet>(pet: TPet, voice: (pet: TPet) => string): string {
|
||||
>speak : <TPet extends Pet>(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
|
||||
}
|
18
tests/cases/compiler/narrowingConstrainedTypeParameter.ts
Normal file
18
tests/cases/compiler/narrowingConstrainedTypeParameter.ts
Normal file
|
@ -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<TPet extends Pet>(pet: TPet, voice: (pet: TPet) => string): string {
|
||||
if (!isPet(pet)) {
|
||||
throw new Error("Expected \"pet\" to be a Pet");
|
||||
}
|
||||
return voice(pet);
|
||||
}
|
Loading…
Reference in a new issue