mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-04 21:53:42 -06:00
Mark deep indexed access comparisons as expanding (#33144)
* mark deep indexed accesses as deeply nested in comparisons * Add test derived from lodash example
This commit is contained in:
parent
2b153fc75a
commit
72bb4c2bdc
@ -14549,6 +14549,9 @@ namespace ts {
|
||||
// though highly unlikely, for this test to be true in a situation where a chain of instantiations is not infinitely
|
||||
// expanding. Effectively, we will generate a false positive when two types are structurally equal to at least 5
|
||||
// levels, but unequal at some level beyond that.
|
||||
// In addition, this will also detect when an indexed access has been chained off of 5 or more times (which is essentially
|
||||
// the dual of the structural comparison), and likewise mark the type as deeply nested, potentially adding false positives
|
||||
// for finite but deeply expanding indexed accesses (eg, for `Q[P1][P2][P3][P4][P5]`).
|
||||
function isDeeplyNestedType(type: Type, stack: Type[], depth: number): boolean {
|
||||
// We track all object types that have an associated symbol (representing the origin of the type)
|
||||
if (depth >= 5 && type.flags & TypeFlags.Object) {
|
||||
@ -14564,9 +14567,31 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (depth >= 5 && type.flags & TypeFlags.IndexedAccess) {
|
||||
const root = getRootObjectTypeFromIndexedAccessChain(type);
|
||||
let count = 0;
|
||||
for (let i = 0; i < depth; i++) {
|
||||
const t = stack[i];
|
||||
if (getRootObjectTypeFromIndexedAccessChain(t) === root) {
|
||||
count++;
|
||||
if (count >= 5) return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the leftmost object type in a chain of indexed accesses, eg, in A[P][Q], returns A
|
||||
*/
|
||||
function getRootObjectTypeFromIndexedAccessChain(type: Type) {
|
||||
let t = type;
|
||||
while (t.flags & TypeFlags.IndexedAccess) {
|
||||
t = (t as IndexedAccessType).objectType;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
function isPropertyIdenticalTo(sourceProp: Symbol, targetProp: Symbol): boolean {
|
||||
return compareProperties(sourceProp, targetProp, compareTypesIdentical) !== Ternary.False;
|
||||
}
|
||||
|
||||
@ -0,0 +1,17 @@
|
||||
//// [comparisonOfPartialDeepAndIndexedAccessTerminatesWithoutError.ts]
|
||||
type PartialDeep<T> = {[K in keyof T]?: PartialDeep<T[K]>};
|
||||
type Many<T> = T | readonly T[];
|
||||
|
||||
interface Collection<T> {
|
||||
sortBy(...iteratees: Many<PartialDeep<T>>[]): Collection<T>;
|
||||
}
|
||||
|
||||
const x: Collection<{x: number}> = (null as any as Collection<{x: number, y: number}>);
|
||||
|
||||
export {};
|
||||
|
||||
|
||||
//// [comparisonOfPartialDeepAndIndexedAccessTerminatesWithoutError.js]
|
||||
"use strict";
|
||||
exports.__esModule = true;
|
||||
var x = null;
|
||||
@ -0,0 +1,40 @@
|
||||
=== tests/cases/compiler/comparisonOfPartialDeepAndIndexedAccessTerminatesWithoutError.ts ===
|
||||
type PartialDeep<T> = {[K in keyof T]?: PartialDeep<T[K]>};
|
||||
>PartialDeep : Symbol(PartialDeep, Decl(comparisonOfPartialDeepAndIndexedAccessTerminatesWithoutError.ts, 0, 0))
|
||||
>T : Symbol(T, Decl(comparisonOfPartialDeepAndIndexedAccessTerminatesWithoutError.ts, 0, 17))
|
||||
>K : Symbol(K, Decl(comparisonOfPartialDeepAndIndexedAccessTerminatesWithoutError.ts, 0, 24))
|
||||
>T : Symbol(T, Decl(comparisonOfPartialDeepAndIndexedAccessTerminatesWithoutError.ts, 0, 17))
|
||||
>PartialDeep : Symbol(PartialDeep, Decl(comparisonOfPartialDeepAndIndexedAccessTerminatesWithoutError.ts, 0, 0))
|
||||
>T : Symbol(T, Decl(comparisonOfPartialDeepAndIndexedAccessTerminatesWithoutError.ts, 0, 17))
|
||||
>K : Symbol(K, Decl(comparisonOfPartialDeepAndIndexedAccessTerminatesWithoutError.ts, 0, 24))
|
||||
|
||||
type Many<T> = T | readonly T[];
|
||||
>Many : Symbol(Many, Decl(comparisonOfPartialDeepAndIndexedAccessTerminatesWithoutError.ts, 0, 59))
|
||||
>T : Symbol(T, Decl(comparisonOfPartialDeepAndIndexedAccessTerminatesWithoutError.ts, 1, 10))
|
||||
>T : Symbol(T, Decl(comparisonOfPartialDeepAndIndexedAccessTerminatesWithoutError.ts, 1, 10))
|
||||
>T : Symbol(T, Decl(comparisonOfPartialDeepAndIndexedAccessTerminatesWithoutError.ts, 1, 10))
|
||||
|
||||
interface Collection<T> {
|
||||
>Collection : Symbol(Collection, Decl(comparisonOfPartialDeepAndIndexedAccessTerminatesWithoutError.ts, 1, 32))
|
||||
>T : Symbol(T, Decl(comparisonOfPartialDeepAndIndexedAccessTerminatesWithoutError.ts, 3, 21))
|
||||
|
||||
sortBy(...iteratees: Many<PartialDeep<T>>[]): Collection<T>;
|
||||
>sortBy : Symbol(Collection.sortBy, Decl(comparisonOfPartialDeepAndIndexedAccessTerminatesWithoutError.ts, 3, 25))
|
||||
>iteratees : Symbol(iteratees, Decl(comparisonOfPartialDeepAndIndexedAccessTerminatesWithoutError.ts, 4, 11))
|
||||
>Many : Symbol(Many, Decl(comparisonOfPartialDeepAndIndexedAccessTerminatesWithoutError.ts, 0, 59))
|
||||
>PartialDeep : Symbol(PartialDeep, Decl(comparisonOfPartialDeepAndIndexedAccessTerminatesWithoutError.ts, 0, 0))
|
||||
>T : Symbol(T, Decl(comparisonOfPartialDeepAndIndexedAccessTerminatesWithoutError.ts, 3, 21))
|
||||
>Collection : Symbol(Collection, Decl(comparisonOfPartialDeepAndIndexedAccessTerminatesWithoutError.ts, 1, 32))
|
||||
>T : Symbol(T, Decl(comparisonOfPartialDeepAndIndexedAccessTerminatesWithoutError.ts, 3, 21))
|
||||
}
|
||||
|
||||
const x: Collection<{x: number}> = (null as any as Collection<{x: number, y: number}>);
|
||||
>x : Symbol(x, Decl(comparisonOfPartialDeepAndIndexedAccessTerminatesWithoutError.ts, 7, 5))
|
||||
>Collection : Symbol(Collection, Decl(comparisonOfPartialDeepAndIndexedAccessTerminatesWithoutError.ts, 1, 32))
|
||||
>x : Symbol(x, Decl(comparisonOfPartialDeepAndIndexedAccessTerminatesWithoutError.ts, 7, 21))
|
||||
>Collection : Symbol(Collection, Decl(comparisonOfPartialDeepAndIndexedAccessTerminatesWithoutError.ts, 1, 32))
|
||||
>x : Symbol(x, Decl(comparisonOfPartialDeepAndIndexedAccessTerminatesWithoutError.ts, 7, 63))
|
||||
>y : Symbol(y, Decl(comparisonOfPartialDeepAndIndexedAccessTerminatesWithoutError.ts, 7, 73))
|
||||
|
||||
export {};
|
||||
|
||||
@ -0,0 +1,25 @@
|
||||
=== tests/cases/compiler/comparisonOfPartialDeepAndIndexedAccessTerminatesWithoutError.ts ===
|
||||
type PartialDeep<T> = {[K in keyof T]?: PartialDeep<T[K]>};
|
||||
>PartialDeep : PartialDeep<T>
|
||||
|
||||
type Many<T> = T | readonly T[];
|
||||
>Many : Many<T>
|
||||
|
||||
interface Collection<T> {
|
||||
sortBy(...iteratees: Many<PartialDeep<T>>[]): Collection<T>;
|
||||
>sortBy : (...iteratees: Many<PartialDeep<T>>[]) => Collection<T>
|
||||
>iteratees : Many<PartialDeep<T>>[]
|
||||
}
|
||||
|
||||
const x: Collection<{x: number}> = (null as any as Collection<{x: number, y: number}>);
|
||||
>x : Collection<{ x: number; }>
|
||||
>x : number
|
||||
>(null as any as Collection<{x: number, y: number}>) : Collection<{ x: number; y: number; }>
|
||||
>null as any as Collection<{x: number, y: number}> : Collection<{ x: number; y: number; }>
|
||||
>null as any : any
|
||||
>null : null
|
||||
>x : number
|
||||
>y : number
|
||||
|
||||
export {};
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
// @strict: true
|
||||
type PartialDeep<T> = {[K in keyof T]?: PartialDeep<T[K]>};
|
||||
type Many<T> = T | readonly T[];
|
||||
|
||||
interface Collection<T> {
|
||||
sortBy(...iteratees: Many<PartialDeep<T>>[]): Collection<T>;
|
||||
}
|
||||
|
||||
const x: Collection<{x: number}> = (null as any as Collection<{x: number, y: number}>);
|
||||
|
||||
export {};
|
||||
Loading…
x
Reference in New Issue
Block a user