Merge pull request #14472 from Microsoft/typeParameterTypeGuards
Fix type parameter type guards
This commit is contained in:
commit
c968eed968
|
@ -9863,9 +9863,8 @@ namespace ts {
|
||||||
if (flags & TypeFlags.NonPrimitive) {
|
if (flags & TypeFlags.NonPrimitive) {
|
||||||
return strictNullChecks ? TypeFacts.ObjectStrictFacts : TypeFacts.ObjectFacts;
|
return strictNullChecks ? TypeFacts.ObjectStrictFacts : TypeFacts.ObjectFacts;
|
||||||
}
|
}
|
||||||
if (flags & TypeFlags.TypeParameter) {
|
if (flags & TypeFlags.TypeVariable) {
|
||||||
const constraint = getConstraintOfTypeParameter(<TypeParameter>type);
|
return getTypeFacts(getBaseConstraintOfType(<TypeVariable>type) || emptyObjectType);
|
||||||
return getTypeFacts(constraint || emptyObjectType);
|
|
||||||
}
|
}
|
||||||
if (flags & TypeFlags.UnionOrIntersection) {
|
if (flags & TypeFlags.UnionOrIntersection) {
|
||||||
return getTypeFactsOfTypes((<UnionOrIntersectionType>type).types);
|
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
|
// is a supertype of that primitive type. For example, type 'any' can be narrowed
|
||||||
// to one of the primitive types.
|
// to one of the primitive types.
|
||||||
const targetType = typeofTypesByName.get(literal.text);
|
const targetType = typeofTypesByName.get(literal.text);
|
||||||
if (targetType && isTypeSubtypeOf(targetType, type)) {
|
if (targetType) {
|
||||||
return 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 ?
|
const facts = assumeTrue ?
|
||||||
|
@ -10774,10 +10781,9 @@ namespace ts {
|
||||||
// Otherwise, if the candidate type is assignable to the target type, narrow to the candidate
|
// 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
|
// type. Otherwise, the types are completely unrelated, so narrow to an intersection of the
|
||||||
// two types.
|
// two types.
|
||||||
const targetType = type.flags & TypeFlags.TypeParameter ? getApparentType(type) : type;
|
|
||||||
return isTypeSubtypeOf(candidate, type) ? candidate :
|
return isTypeSubtypeOf(candidate, type) ? candidate :
|
||||||
isTypeAssignableTo(type, candidate) ? type :
|
isTypeAssignableTo(type, candidate) ? type :
|
||||||
isTypeAssignableTo(candidate, targetType) ? candidate :
|
isTypeAssignableTo(candidate, type) ? candidate :
|
||||||
getIntersectionType([type, candidate]);
|
getIntersectionType([type, candidate]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
68
tests/baselines/reference/typeGuardsTypeParameters.js
Normal file
68
tests/baselines/reference/typeGuardsTypeParameters.js
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
98
tests/baselines/reference/typeGuardsTypeParameters.symbols
Normal file
98
tests/baselines/reference/typeGuardsTypeParameters.symbols
Normal 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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
108
tests/baselines/reference/typeGuardsTypeParameters.types
Normal file
108
tests/baselines/reference/typeGuardsTypeParameters.types
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue