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) {
|
||||
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,9 +10674,17 @@ 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)) {
|
||||
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 ?
|
||||
typeofEQFacts.get(literal.text) || TypeFacts.TypeofEQHostObject :
|
||||
|
@ -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]);
|
||||
}
|
||||
|
||||
|
|
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