Merge pull request #14472 from Microsoft/typeParameterTypeGuards

Fix type parameter type guards
This commit is contained in:
Anders Hejlsberg 2017-03-06 11:12:17 -10:00 committed by GitHub
commit c968eed968
5 changed files with 322 additions and 7 deletions

View file

@ -9863,9 +9863,8 @@ namespace ts {
if (flags & TypeFlags.NonPrimitive) {
return strictNullChecks ? TypeFacts.ObjectStrictFacts : TypeFacts.ObjectFacts;
}
if (flags & TypeFlags.TypeParameter) {
const constraint = getConstraintOfTypeParameter(<TypeParameter>type);
return getTypeFacts(constraint || emptyObjectType);
if (flags & TypeFlags.TypeVariable) {
return getTypeFacts(getBaseConstraintOfType(<TypeVariable>type) || emptyObjectType);
}
if (flags & TypeFlags.UnionOrIntersection) {
return getTypeFactsOfTypes((<UnionOrIntersectionType>type).types);
@ -10675,8 +10674,16 @@ namespace ts {
// is a supertype of that primitive type. For example, type 'any' can be narrowed
// to one of the primitive types.
const targetType = typeofTypesByName.get(literal.text);
if (targetType && isTypeSubtypeOf(targetType, type)) {
return targetType;
if (targetType) {
if (isTypeSubtypeOf(targetType, type)) {
return targetType;
}
if (type.flags & TypeFlags.TypeVariable) {
const constraint = getBaseConstraintOfType(<TypeVariable>type) || anyType;
if (isTypeSubtypeOf(targetType, constraint)) {
return getIntersectionType([type, targetType]);
}
}
}
}
const facts = assumeTrue ?
@ -10774,10 +10781,9 @@ namespace ts {
// Otherwise, if the candidate type is assignable to the target type, narrow to the candidate
// 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, type) ? candidate :
isTypeAssignableTo(type, candidate) ? type :
isTypeAssignableTo(candidate, targetType) ? candidate :
isTypeAssignableTo(candidate, type) ? candidate :
getIntersectionType([type, candidate]);
}

View file

@ -0,0 +1,68 @@
//// [typeGuardsTypeParameters.ts]
// Type guards involving type parameters produce intersection types
class C {
prop: string;
}
function f1<T>(x: T) {
if (x instanceof C) {
let v1: T = x;
let v2: C = x;
x.prop;
}
}
function f2<T>(x: T) {
if (typeof x === "string") {
let v1: T = x;
let v2: string = x;
x.length;
}
}
// Repro from #13872
function fun<T>(item: { [P in keyof T]: T[P] }) {
const strings: string[] = [];
for (const key in item) {
const value = item[key];
if (typeof value === "string") {
strings.push(value);
}
}
}
//// [typeGuardsTypeParameters.js]
// Type guards involving type parameters produce intersection types
var C = (function () {
function C() {
}
return C;
}());
function f1(x) {
if (x instanceof C) {
var v1 = x;
var v2 = x;
x.prop;
}
}
function f2(x) {
if (typeof x === "string") {
var v1 = x;
var v2 = x;
x.length;
}
}
// Repro from #13872
function fun(item) {
var strings = [];
for (var key in item) {
var value = item[key];
if (typeof value === "string") {
strings.push(value);
}
}
}

View file

@ -0,0 +1,98 @@
=== tests/cases/conformance/controlFlow/typeGuardsTypeParameters.ts ===
// Type guards involving type parameters produce intersection types
class C {
>C : Symbol(C, Decl(typeGuardsTypeParameters.ts, 0, 0))
prop: string;
>prop : Symbol(C.prop, Decl(typeGuardsTypeParameters.ts, 3, 9))
}
function f1<T>(x: T) {
>f1 : Symbol(f1, Decl(typeGuardsTypeParameters.ts, 5, 1))
>T : Symbol(T, Decl(typeGuardsTypeParameters.ts, 7, 12))
>x : Symbol(x, Decl(typeGuardsTypeParameters.ts, 7, 15))
>T : Symbol(T, Decl(typeGuardsTypeParameters.ts, 7, 12))
if (x instanceof C) {
>x : Symbol(x, Decl(typeGuardsTypeParameters.ts, 7, 15))
>C : Symbol(C, Decl(typeGuardsTypeParameters.ts, 0, 0))
let v1: T = x;
>v1 : Symbol(v1, Decl(typeGuardsTypeParameters.ts, 9, 11))
>T : Symbol(T, Decl(typeGuardsTypeParameters.ts, 7, 12))
>x : Symbol(x, Decl(typeGuardsTypeParameters.ts, 7, 15))
let v2: C = x;
>v2 : Symbol(v2, Decl(typeGuardsTypeParameters.ts, 10, 11))
>C : Symbol(C, Decl(typeGuardsTypeParameters.ts, 0, 0))
>x : Symbol(x, Decl(typeGuardsTypeParameters.ts, 7, 15))
x.prop;
>x.prop : Symbol(C.prop, Decl(typeGuardsTypeParameters.ts, 3, 9))
>x : Symbol(x, Decl(typeGuardsTypeParameters.ts, 7, 15))
>prop : Symbol(C.prop, Decl(typeGuardsTypeParameters.ts, 3, 9))
}
}
function f2<T>(x: T) {
>f2 : Symbol(f2, Decl(typeGuardsTypeParameters.ts, 13, 1))
>T : Symbol(T, Decl(typeGuardsTypeParameters.ts, 15, 12))
>x : Symbol(x, Decl(typeGuardsTypeParameters.ts, 15, 15))
>T : Symbol(T, Decl(typeGuardsTypeParameters.ts, 15, 12))
if (typeof x === "string") {
>x : Symbol(x, Decl(typeGuardsTypeParameters.ts, 15, 15))
let v1: T = x;
>v1 : Symbol(v1, Decl(typeGuardsTypeParameters.ts, 17, 11))
>T : Symbol(T, Decl(typeGuardsTypeParameters.ts, 15, 12))
>x : Symbol(x, Decl(typeGuardsTypeParameters.ts, 15, 15))
let v2: string = x;
>v2 : Symbol(v2, Decl(typeGuardsTypeParameters.ts, 18, 11))
>x : Symbol(x, Decl(typeGuardsTypeParameters.ts, 15, 15))
x.length;
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(typeGuardsTypeParameters.ts, 15, 15))
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
}
}
// Repro from #13872
function fun<T>(item: { [P in keyof T]: T[P] }) {
>fun : Symbol(fun, Decl(typeGuardsTypeParameters.ts, 21, 1))
>T : Symbol(T, Decl(typeGuardsTypeParameters.ts, 25, 13))
>item : Symbol(item, Decl(typeGuardsTypeParameters.ts, 25, 16))
>P : Symbol(P, Decl(typeGuardsTypeParameters.ts, 25, 25))
>T : Symbol(T, Decl(typeGuardsTypeParameters.ts, 25, 13))
>T : Symbol(T, Decl(typeGuardsTypeParameters.ts, 25, 13))
>P : Symbol(P, Decl(typeGuardsTypeParameters.ts, 25, 25))
const strings: string[] = [];
>strings : Symbol(strings, Decl(typeGuardsTypeParameters.ts, 26, 9))
for (const key in item) {
>key : Symbol(key, Decl(typeGuardsTypeParameters.ts, 27, 14))
>item : Symbol(item, Decl(typeGuardsTypeParameters.ts, 25, 16))
const value = item[key];
>value : Symbol(value, Decl(typeGuardsTypeParameters.ts, 28, 13))
>item : Symbol(item, Decl(typeGuardsTypeParameters.ts, 25, 16))
>key : Symbol(key, Decl(typeGuardsTypeParameters.ts, 27, 14))
if (typeof value === "string") {
>value : Symbol(value, Decl(typeGuardsTypeParameters.ts, 28, 13))
strings.push(value);
>strings.push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>strings : Symbol(strings, Decl(typeGuardsTypeParameters.ts, 26, 9))
>push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>value : Symbol(value, Decl(typeGuardsTypeParameters.ts, 28, 13))
}
}
}

View file

@ -0,0 +1,108 @@
=== tests/cases/conformance/controlFlow/typeGuardsTypeParameters.ts ===
// Type guards involving type parameters produce intersection types
class C {
>C : C
prop: string;
>prop : string
}
function f1<T>(x: T) {
>f1 : <T>(x: T) => void
>T : T
>x : T
>T : T
if (x instanceof C) {
>x instanceof C : boolean
>x : T
>C : typeof C
let v1: T = x;
>v1 : T
>T : T
>x : T & C
let v2: C = x;
>v2 : C
>C : C
>x : T & C
x.prop;
>x.prop : string
>x : T & C
>prop : string
}
}
function f2<T>(x: T) {
>f2 : <T>(x: T) => void
>T : T
>x : T
>T : T
if (typeof x === "string") {
>typeof x === "string" : boolean
>typeof x : "string" | "number" | "boolean" | "symbol" | "undefined" | "object" | "function"
>x : T
>"string" : "string"
let v1: T = x;
>v1 : T
>T : T
>x : T & string
let v2: string = x;
>v2 : string
>x : T & string
x.length;
>x.length : number
>x : T & string
>length : number
}
}
// Repro from #13872
function fun<T>(item: { [P in keyof T]: T[P] }) {
>fun : <T>(item: { [P in keyof T]: T[P]; }) => void
>T : T
>item : { [P in keyof T]: T[P]; }
>P : P
>T : T
>T : T
>P : P
const strings: string[] = [];
>strings : string[]
>[] : never[]
for (const key in item) {
>key : keyof T
>item : { [P in keyof T]: T[P]; }
const value = item[key];
>value : T[keyof T]
>item[key] : T[keyof T]
>item : { [P in keyof T]: T[P]; }
>key : keyof T
if (typeof value === "string") {
>typeof value === "string" : boolean
>typeof value : "string" | "number" | "boolean" | "symbol" | "undefined" | "object" | "function"
>value : T[keyof T]
>"string" : "string"
strings.push(value);
>strings.push(value) : number
>strings.push : (...items: string[]) => number
>strings : string[]
>push : (...items: string[]) => number
>value : T[keyof T] & string
}
}
}

View file

@ -0,0 +1,35 @@
// @strictNullChecks: true
// Type guards involving type parameters produce intersection types
class C {
prop: string;
}
function f1<T>(x: T) {
if (x instanceof C) {
let v1: T = x;
let v2: C = x;
x.prop;
}
}
function f2<T>(x: T) {
if (typeof x === "string") {
let v1: T = x;
let v2: string = x;
x.length;
}
}
// Repro from #13872
function fun<T>(item: { [P in keyof T]: T[P] }) {
const strings: string[] = [];
for (const key in item) {
const value = item[key];
if (typeof value === "string") {
strings.push(value);
}
}
}