Skip to content

Commit df4b02a

Browse files
committed
Go 5 levels deep when determining if an object type is deeply nested
Fixes #56138, but at what cost...
1 parent 521f8a8 commit df4b02a

8 files changed

+488
-17
lines changed

src/compiler/checker.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21769,12 +21769,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2176921769
if (recursionFlags & RecursionFlags.Source) {
2177021770
sourceStack[sourceDepth] = source;
2177121771
sourceDepth++;
21772-
if (!(expandingFlags & ExpandingFlags.Source) && isDeeplyNestedType(source, sourceStack, sourceDepth)) expandingFlags |= ExpandingFlags.Source;
21772+
if (!(expandingFlags & ExpandingFlags.Source) && isDeeplyNestedType(source, sourceStack, sourceDepth, 5)) expandingFlags |= ExpandingFlags.Source;
2177321773
}
2177421774
if (recursionFlags & RecursionFlags.Target) {
2177521775
targetStack[targetDepth] = target;
2177621776
targetDepth++;
21777-
if (!(expandingFlags & ExpandingFlags.Target) && isDeeplyNestedType(target, targetStack, targetDepth)) expandingFlags |= ExpandingFlags.Target;
21777+
if (!(expandingFlags & ExpandingFlags.Target) && isDeeplyNestedType(target, targetStack, targetDepth, 5)) expandingFlags |= ExpandingFlags.Target;
2177821778
}
2177921779
let originalHandler: typeof outofbandVarianceMarkerHandler;
2178021780
let propagatingVarianceFlags = 0 as RelationComparisonResult;
@@ -23577,7 +23577,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2357723577
// `type A<T> = null extends T ? [A<NonNullable<T>>] : [T]`
2357823578
// has expanded into `[A<NonNullable<NonNullable<NonNullable<NonNullable<NonNullable<T>>>>>>]`. In such cases we need
2357923579
// to terminate the expansion, and we do so here.
23580-
function isDeeplyNestedType(type: Type, stack: Type[], depth: number, maxDepth = 3): boolean {
23580+
function isDeeplyNestedType(type: Type, stack: Type[], depth: number, maxDepth: number): boolean {
2358123581
if (depth >= maxDepth) {
2358223582
if (type.flags & TypeFlags.Intersection) {
2358323583
return some((type as IntersectionType).types, t => isDeeplyNestedType(t, stack, depth, maxDepth));
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
deepObjectInstantiations.ts(24,5): error TS2322: Type '{ level1: { level2: { foo: string; }; }; }' is not assignable to type '{ level1: { level2: { foo: string; bar: string; }; }; }'.
2+
The types of 'level1.level2' are incompatible between these types.
3+
Property 'bar' is missing in type '{ foo: string; }' but required in type '{ foo: string; bar: string; }'.
4+
5+
6+
==== deepObjectInstantiations.ts (1 errors) ====
7+
// @strict
8+
9+
export type Input = Static<typeof Input, []>
10+
export const Input = MakeObject({
11+
level1: MakeObject({
12+
level2: MakeObject({
13+
foo: MakeString(),
14+
})
15+
})
16+
})
17+
18+
export type Output = Static<typeof Output, []>
19+
export const Output = MakeObject({
20+
level1: MakeObject({
21+
level2: MakeObject({
22+
foo: MakeString(),
23+
bar: MakeString(),
24+
})
25+
})
26+
})
27+
28+
function problematicFunction1(ors: Input): Output {
29+
// Should error
30+
return ors;
31+
~~~~~~
32+
!!! error TS2322: Type '{ level1: { level2: { foo: string; }; }; }' is not assignable to type '{ level1: { level2: { foo: string; bar: string; }; }; }'.
33+
!!! error TS2322: The types of 'level1.level2' are incompatible between these types.
34+
!!! error TS2322: Property 'bar' is missing in type '{ foo: string; }' but required in type '{ foo: string; bar: string; }'.
35+
!!! related TS2728 deepObjectInstantiations.ts:17:13: 'bar' is declared here.
36+
}
37+
function f() {
38+
problematicFunction1(null as any);
39+
}
40+
f();
41+
42+
export type Evaluate<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;
43+
interface HasStatic { static: unknown }
44+
interface HasParams { params: unknown[] }
45+
export type Static<T extends HasStatic, P> = (T & { params: P; })['static']
46+
47+
type RecordOfHasStatics = Record<string, HasStatic>;
48+
49+
export type PropertiesReduce<T extends RecordOfHasStatics, P = []> = Evaluate<{ [K in keyof T]: Static<T[K], P> }>;
50+
51+
declare function MakeObject<T extends RecordOfHasStatics>(object: T): TObject<T>;
52+
export interface TObject<T extends RecordOfHasStatics> extends HasParams {
53+
static: PropertiesReduce<T, this['params']>;
54+
properties: T;
55+
}
56+
57+
declare function MakeString(): HasParams & { static: string };
58+
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
//// [tests/cases/compiler/deepObjectInstantiations.ts] ////
2+
3+
//// [deepObjectInstantiations.ts]
4+
// @strict
5+
6+
export type Input = Static<typeof Input, []>
7+
export const Input = MakeObject({
8+
level1: MakeObject({
9+
level2: MakeObject({
10+
foo: MakeString(),
11+
})
12+
})
13+
})
14+
15+
export type Output = Static<typeof Output, []>
16+
export const Output = MakeObject({
17+
level1: MakeObject({
18+
level2: MakeObject({
19+
foo: MakeString(),
20+
bar: MakeString(),
21+
})
22+
})
23+
})
24+
25+
function problematicFunction1(ors: Input): Output {
26+
// Should error
27+
return ors;
28+
}
29+
function f() {
30+
problematicFunction1(null as any);
31+
}
32+
f();
33+
34+
export type Evaluate<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;
35+
interface HasStatic { static: unknown }
36+
interface HasParams { params: unknown[] }
37+
export type Static<T extends HasStatic, P> = (T & { params: P; })['static']
38+
39+
type RecordOfHasStatics = Record<string, HasStatic>;
40+
41+
export type PropertiesReduce<T extends RecordOfHasStatics, P = []> = Evaluate<{ [K in keyof T]: Static<T[K], P> }>;
42+
43+
declare function MakeObject<T extends RecordOfHasStatics>(object: T): TObject<T>;
44+
export interface TObject<T extends RecordOfHasStatics> extends HasParams {
45+
static: PropertiesReduce<T, this['params']>;
46+
properties: T;
47+
}
48+
49+
declare function MakeString(): HasParams & { static: string };
50+
51+
52+
//// [deepObjectInstantiations.js]
53+
"use strict";
54+
// @strict
55+
Object.defineProperty(exports, "__esModule", { value: true });
56+
exports.Output = exports.Input = void 0;
57+
exports.Input = MakeObject({
58+
level1: MakeObject({
59+
level2: MakeObject({
60+
foo: MakeString(),
61+
})
62+
})
63+
});
64+
exports.Output = MakeObject({
65+
level1: MakeObject({
66+
level2: MakeObject({
67+
foo: MakeString(),
68+
bar: MakeString(),
69+
})
70+
})
71+
});
72+
function problematicFunction1(ors) {
73+
// Should error
74+
return ors;
75+
}
76+
function f() {
77+
problematicFunction1(null);
78+
}
79+
f();
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
//// [tests/cases/compiler/deepObjectInstantiations.ts] ////
2+
3+
=== deepObjectInstantiations.ts ===
4+
// @strict
5+
6+
export type Input = Static<typeof Input, []>
7+
>Input : Symbol(Input, Decl(deepObjectInstantiations.ts, 0, 0), Decl(deepObjectInstantiations.ts, 3, 12))
8+
>Static : Symbol(Static, Decl(deepObjectInstantiations.ts, 32, 41))
9+
>Input : Symbol(Input, Decl(deepObjectInstantiations.ts, 0, 0), Decl(deepObjectInstantiations.ts, 3, 12))
10+
11+
export const Input = MakeObject({
12+
>Input : Symbol(Input, Decl(deepObjectInstantiations.ts, 0, 0), Decl(deepObjectInstantiations.ts, 3, 12))
13+
>MakeObject : Symbol(MakeObject, Decl(deepObjectInstantiations.ts, 37, 115))
14+
15+
level1: MakeObject({
16+
>level1 : Symbol(level1, Decl(deepObjectInstantiations.ts, 3, 33))
17+
>MakeObject : Symbol(MakeObject, Decl(deepObjectInstantiations.ts, 37, 115))
18+
19+
level2: MakeObject({
20+
>level2 : Symbol(level2, Decl(deepObjectInstantiations.ts, 4, 24))
21+
>MakeObject : Symbol(MakeObject, Decl(deepObjectInstantiations.ts, 37, 115))
22+
23+
foo: MakeString(),
24+
>foo : Symbol(foo, Decl(deepObjectInstantiations.ts, 5, 28))
25+
>MakeString : Symbol(MakeString, Decl(deepObjectInstantiations.ts, 43, 1))
26+
27+
})
28+
})
29+
})
30+
31+
export type Output = Static<typeof Output, []>
32+
>Output : Symbol(Output, Decl(deepObjectInstantiations.ts, 9, 2), Decl(deepObjectInstantiations.ts, 12, 12))
33+
>Static : Symbol(Static, Decl(deepObjectInstantiations.ts, 32, 41))
34+
>Output : Symbol(Output, Decl(deepObjectInstantiations.ts, 9, 2), Decl(deepObjectInstantiations.ts, 12, 12))
35+
36+
export const Output = MakeObject({
37+
>Output : Symbol(Output, Decl(deepObjectInstantiations.ts, 9, 2), Decl(deepObjectInstantiations.ts, 12, 12))
38+
>MakeObject : Symbol(MakeObject, Decl(deepObjectInstantiations.ts, 37, 115))
39+
40+
level1: MakeObject({
41+
>level1 : Symbol(level1, Decl(deepObjectInstantiations.ts, 12, 34))
42+
>MakeObject : Symbol(MakeObject, Decl(deepObjectInstantiations.ts, 37, 115))
43+
44+
level2: MakeObject({
45+
>level2 : Symbol(level2, Decl(deepObjectInstantiations.ts, 13, 24))
46+
>MakeObject : Symbol(MakeObject, Decl(deepObjectInstantiations.ts, 37, 115))
47+
48+
foo: MakeString(),
49+
>foo : Symbol(foo, Decl(deepObjectInstantiations.ts, 14, 28))
50+
>MakeString : Symbol(MakeString, Decl(deepObjectInstantiations.ts, 43, 1))
51+
52+
bar: MakeString(),
53+
>bar : Symbol(bar, Decl(deepObjectInstantiations.ts, 15, 30))
54+
>MakeString : Symbol(MakeString, Decl(deepObjectInstantiations.ts, 43, 1))
55+
56+
})
57+
})
58+
})
59+
60+
function problematicFunction1(ors: Input): Output {
61+
>problematicFunction1 : Symbol(problematicFunction1, Decl(deepObjectInstantiations.ts, 19, 2))
62+
>ors : Symbol(ors, Decl(deepObjectInstantiations.ts, 21, 30))
63+
>Input : Symbol(Input, Decl(deepObjectInstantiations.ts, 0, 0), Decl(deepObjectInstantiations.ts, 3, 12))
64+
>Output : Symbol(Output, Decl(deepObjectInstantiations.ts, 9, 2), Decl(deepObjectInstantiations.ts, 12, 12))
65+
66+
// Should error
67+
return ors;
68+
>ors : Symbol(ors, Decl(deepObjectInstantiations.ts, 21, 30))
69+
}
70+
function f() {
71+
>f : Symbol(f, Decl(deepObjectInstantiations.ts, 24, 1))
72+
73+
problematicFunction1(null as any);
74+
>problematicFunction1 : Symbol(problematicFunction1, Decl(deepObjectInstantiations.ts, 19, 2))
75+
}
76+
f();
77+
>f : Symbol(f, Decl(deepObjectInstantiations.ts, 24, 1))
78+
79+
export type Evaluate<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;
80+
>Evaluate : Symbol(Evaluate, Decl(deepObjectInstantiations.ts, 28, 4))
81+
>T : Symbol(T, Decl(deepObjectInstantiations.ts, 30, 21))
82+
>T : Symbol(T, Decl(deepObjectInstantiations.ts, 30, 21))
83+
>O : Symbol(O, Decl(deepObjectInstantiations.ts, 30, 41))
84+
>K : Symbol(K, Decl(deepObjectInstantiations.ts, 30, 49))
85+
>O : Symbol(O, Decl(deepObjectInstantiations.ts, 30, 41))
86+
>O : Symbol(O, Decl(deepObjectInstantiations.ts, 30, 41))
87+
>K : Symbol(K, Decl(deepObjectInstantiations.ts, 30, 49))
88+
89+
interface HasStatic { static: unknown }
90+
>HasStatic : Symbol(HasStatic, Decl(deepObjectInstantiations.ts, 30, 79))
91+
>static : Symbol(HasStatic.static, Decl(deepObjectInstantiations.ts, 31, 21))
92+
93+
interface HasParams { params: unknown[] }
94+
>HasParams : Symbol(HasParams, Decl(deepObjectInstantiations.ts, 31, 39))
95+
>params : Symbol(HasParams.params, Decl(deepObjectInstantiations.ts, 32, 21))
96+
97+
export type Static<T extends HasStatic, P> = (T & { params: P; })['static']
98+
>Static : Symbol(Static, Decl(deepObjectInstantiations.ts, 32, 41))
99+
>T : Symbol(T, Decl(deepObjectInstantiations.ts, 33, 19))
100+
>HasStatic : Symbol(HasStatic, Decl(deepObjectInstantiations.ts, 30, 79))
101+
>P : Symbol(P, Decl(deepObjectInstantiations.ts, 33, 39))
102+
>T : Symbol(T, Decl(deepObjectInstantiations.ts, 33, 19))
103+
>params : Symbol(params, Decl(deepObjectInstantiations.ts, 33, 51))
104+
>P : Symbol(P, Decl(deepObjectInstantiations.ts, 33, 39))
105+
106+
type RecordOfHasStatics = Record<string, HasStatic>;
107+
>RecordOfHasStatics : Symbol(RecordOfHasStatics, Decl(deepObjectInstantiations.ts, 33, 75))
108+
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
109+
>HasStatic : Symbol(HasStatic, Decl(deepObjectInstantiations.ts, 30, 79))
110+
111+
export type PropertiesReduce<T extends RecordOfHasStatics, P = []> = Evaluate<{ [K in keyof T]: Static<T[K], P> }>;
112+
>PropertiesReduce : Symbol(PropertiesReduce, Decl(deepObjectInstantiations.ts, 35, 52))
113+
>T : Symbol(T, Decl(deepObjectInstantiations.ts, 37, 29))
114+
>RecordOfHasStatics : Symbol(RecordOfHasStatics, Decl(deepObjectInstantiations.ts, 33, 75))
115+
>P : Symbol(P, Decl(deepObjectInstantiations.ts, 37, 58))
116+
>Evaluate : Symbol(Evaluate, Decl(deepObjectInstantiations.ts, 28, 4))
117+
>K : Symbol(K, Decl(deepObjectInstantiations.ts, 37, 81))
118+
>T : Symbol(T, Decl(deepObjectInstantiations.ts, 37, 29))
119+
>Static : Symbol(Static, Decl(deepObjectInstantiations.ts, 32, 41))
120+
>T : Symbol(T, Decl(deepObjectInstantiations.ts, 37, 29))
121+
>K : Symbol(K, Decl(deepObjectInstantiations.ts, 37, 81))
122+
>P : Symbol(P, Decl(deepObjectInstantiations.ts, 37, 58))
123+
124+
declare function MakeObject<T extends RecordOfHasStatics>(object: T): TObject<T>;
125+
>MakeObject : Symbol(MakeObject, Decl(deepObjectInstantiations.ts, 37, 115))
126+
>T : Symbol(T, Decl(deepObjectInstantiations.ts, 39, 28))
127+
>RecordOfHasStatics : Symbol(RecordOfHasStatics, Decl(deepObjectInstantiations.ts, 33, 75))
128+
>object : Symbol(object, Decl(deepObjectInstantiations.ts, 39, 58))
129+
>T : Symbol(T, Decl(deepObjectInstantiations.ts, 39, 28))
130+
>TObject : Symbol(TObject, Decl(deepObjectInstantiations.ts, 39, 81))
131+
>T : Symbol(T, Decl(deepObjectInstantiations.ts, 39, 28))
132+
133+
export interface TObject<T extends RecordOfHasStatics> extends HasParams {
134+
>TObject : Symbol(TObject, Decl(deepObjectInstantiations.ts, 39, 81))
135+
>T : Symbol(T, Decl(deepObjectInstantiations.ts, 40, 25))
136+
>RecordOfHasStatics : Symbol(RecordOfHasStatics, Decl(deepObjectInstantiations.ts, 33, 75))
137+
>HasParams : Symbol(HasParams, Decl(deepObjectInstantiations.ts, 31, 39))
138+
139+
static: PropertiesReduce<T, this['params']>;
140+
>static : Symbol(TObject.static, Decl(deepObjectInstantiations.ts, 40, 74))
141+
>PropertiesReduce : Symbol(PropertiesReduce, Decl(deepObjectInstantiations.ts, 35, 52))
142+
>T : Symbol(T, Decl(deepObjectInstantiations.ts, 40, 25))
143+
144+
properties: T;
145+
>properties : Symbol(TObject.properties, Decl(deepObjectInstantiations.ts, 41, 48))
146+
>T : Symbol(T, Decl(deepObjectInstantiations.ts, 40, 25))
147+
}
148+
149+
declare function MakeString(): HasParams & { static: string };
150+
>MakeString : Symbol(MakeString, Decl(deepObjectInstantiations.ts, 43, 1))
151+
>HasParams : Symbol(HasParams, Decl(deepObjectInstantiations.ts, 31, 39))
152+
>static : Symbol(static, Decl(deepObjectInstantiations.ts, 45, 44))
153+

0 commit comments

Comments
 (0)