Merge pull request #10839 from Microsoft/fixNarrowingWithTypeParameter

Fix narrowing with constrained type parameter
This commit is contained in:
Mohamed Hegazy 2016-09-12 11:25:21 -07:00 committed by GitHub
commit 1fce34be71
5 changed files with 145 additions and 1 deletions

View file

@ -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]);

View file

@ -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;

View file

@ -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))
}

View file

@ -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
}

View 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);
}