Improve detection of cases where subtype reduction is unnecessary (#53435)

This commit is contained in:
Anders Hejlsberg 2023-03-23 07:09:12 -07:00 committed by GitHub
parent 37bafa539c
commit 511921e1e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 128 additions and 4 deletions

View File

@ -25891,7 +25891,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
function isTypeSubsetOf(source: Type, target: Type) {
return source === target || target.flags & TypeFlags.Union && isTypeSubsetOfUnion(source, target as UnionType);
return !!(source === target || source.flags & TypeFlags.Never || target.flags & TypeFlags.Union && isTypeSubsetOfUnion(source, target as UnionType));
}
function isTypeSubsetOfUnion(source: Type, target: UnionType) {
@ -26715,7 +26715,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// If an antecedent type is not a subset of the declared type, we need to perform
// subtype reduction. This happens when a "foreign" type is injected into the control
// flow using the instanceof operator or a user defined type predicate.
if (!isTypeSubsetOf(type, declaredType)) {
if (!isTypeSubsetOf(type, initialType)) {
subtypeReduction = true;
}
if (isIncomplete(flowType)) {
@ -26733,7 +26733,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return type;
}
antecedentTypes.push(type);
if (!isTypeSubsetOf(type, declaredType)) {
if (!isTypeSubsetOf(type, initialType)) {
subtypeReduction = true;
}
if (isIncomplete(flowType)) {
@ -26808,7 +26808,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// If an antecedent type is not a subset of the declared type, we need to perform
// subtype reduction. This happens when a "foreign" type is injected into the control
// flow using the instanceof operator or a user defined type predicate.
if (!isTypeSubsetOf(type, declaredType)) {
if (!isTypeSubsetOf(type, initialType)) {
subtypeReduction = true;
}
// If the type at a particular antecedent path is the declared type there is no

View File

@ -0,0 +1,51 @@
=== tests/cases/compiler/noSubtypeReduction.ts ===
// Repro from #53425
export interface IA {
>IA : Symbol(IA, Decl(noSubtypeReduction.ts, 0, 0))
arr: { A: number; }[];
>arr : Symbol(IA.arr, Decl(noSubtypeReduction.ts, 2, 21))
>A : Symbol(A, Decl(noSubtypeReduction.ts, 3, 10))
}
export interface IAB {
>IAB : Symbol(IAB, Decl(noSubtypeReduction.ts, 4, 1))
arr: { A: number; B: number; }[];
>arr : Symbol(IAB.arr, Decl(noSubtypeReduction.ts, 6, 22))
>A : Symbol(A, Decl(noSubtypeReduction.ts, 7, 10))
>B : Symbol(B, Decl(noSubtypeReduction.ts, 7, 21))
}
export function F(x: IA | IAB) {
>F : Symbol(F, Decl(noSubtypeReduction.ts, 8, 1))
>x : Symbol(x, Decl(noSubtypeReduction.ts, 10, 18))
>IA : Symbol(IA, Decl(noSubtypeReduction.ts, 0, 0))
>IAB : Symbol(IAB, Decl(noSubtypeReduction.ts, 4, 1))
const useB = (t: number) => { };
>useB : Symbol(useB, Decl(noSubtypeReduction.ts, 11, 9))
>t : Symbol(t, Decl(noSubtypeReduction.ts, 11, 18))
for (const el of x.arr) {
>el : Symbol(el, Decl(noSubtypeReduction.ts, 12, 14))
>x.arr : Symbol(arr, Decl(noSubtypeReduction.ts, 2, 21), Decl(noSubtypeReduction.ts, 6, 22))
>x : Symbol(x, Decl(noSubtypeReduction.ts, 10, 18))
>arr : Symbol(arr, Decl(noSubtypeReduction.ts, 2, 21), Decl(noSubtypeReduction.ts, 6, 22))
if ('A' in el) { }
>el : Symbol(el, Decl(noSubtypeReduction.ts, 12, 14))
if ('B' in el) {
>el : Symbol(el, Decl(noSubtypeReduction.ts, 12, 14))
useB(el.B);
>useB : Symbol(useB, Decl(noSubtypeReduction.ts, 11, 9))
>el.B : Symbol(B, Decl(noSubtypeReduction.ts, 7, 21))
>el : Symbol(el, Decl(noSubtypeReduction.ts, 12, 14))
>B : Symbol(B, Decl(noSubtypeReduction.ts, 7, 21))
}
}
}

View File

@ -0,0 +1,51 @@
=== tests/cases/compiler/noSubtypeReduction.ts ===
// Repro from #53425
export interface IA {
arr: { A: number; }[];
>arr : { A: number; }[]
>A : number
}
export interface IAB {
arr: { A: number; B: number; }[];
>arr : { A: number; B: number; }[]
>A : number
>B : number
}
export function F(x: IA | IAB) {
>F : (x: IA | IAB) => void
>x : IA | IAB
const useB = (t: number) => { };
>useB : (t: number) => void
>(t: number) => { } : (t: number) => void
>t : number
for (const el of x.arr) {
>el : { A: number; } | { A: number; B: number; }
>x.arr : { A: number; }[] | { A: number; B: number; }[]
>x : IA | IAB
>arr : { A: number; }[] | { A: number; B: number; }[]
if ('A' in el) { }
>'A' in el : boolean
>'A' : "A"
>el : { A: number; } | { A: number; B: number; }
if ('B' in el) {
>'B' in el : boolean
>'B' : "B"
>el : { A: number; } | { A: number; B: number; }
useB(el.B);
>useB(el.B) : void
>useB : (t: number) => void
>el.B : number
>el : { A: number; B: number; }
>B : number
}
}
}

View File

@ -0,0 +1,22 @@
// @strict: true
// @noEmit: true
// Repro from #53425
export interface IA {
arr: { A: number; }[];
}
export interface IAB {
arr: { A: number; B: number; }[];
}
export function F(x: IA | IAB) {
const useB = (t: number) => { };
for (const el of x.arr) {
if ('A' in el) { }
if ('B' in el) {
useB(el.B);
}
}
}