Skip to content

Commit 6f5304d

Browse files
authored
Avoid exponential base type search in hasBaseType (#64737)
1 parent 6161772 commit 6f5304d

4 files changed

Lines changed: 969 additions & 2 deletions

File tree

‎tsc/internal/checker/checker.go‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20125,14 +20125,22 @@ func (c *Checker) isValidBaseType(t *Type) bool {
2012520125

2012620126
// TODO: GH#18217 If `checkBase` is undefined, we should not call this because this will always return false.
2012720127
func (c *Checker) hasBaseType(t *Type, checkBase *Type) bool {
20128+
// Paths through base types only branch at types with several base types and at
20129+
// intersections. Searching each of those once keeps shared base types from making
20130+
// the search exponential, and single inheritance allocates nothing.
20131+
var visited collections.Set[*Type]
2012820132
var check func(*Type) bool
2012920133
check = func(t *Type) bool {
2013020134
if t.objectFlags&(ObjectFlagsClassOrInterface|ObjectFlagsReference) != 0 {
2013120135
target := getTargetType(t)
20132-
return target == checkBase || core.Some(c.getBaseTypes(target), check)
20136+
if target == checkBase {
20137+
return true
20138+
}
20139+
baseTypes := c.getBaseTypes(target)
20140+
return (len(baseTypes) < 2 || visited.AddIfAbsent(target)) && core.Some(baseTypes, check)
2013320141
}
2013420142
if t.flags&TypeFlagsIntersection != 0 {
20135-
return core.Some(t.Types(), check)
20143+
return visited.AddIfAbsent(t) && core.Some(t.Types(), check)
2013620144
}
2013720145
return false
2013820146
}
Lines changed: 324 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,324 @@
1+
sharedInterfaceBaseTypes.ts(304,11): error TS2310: Type 'Cycle1' recursively references itself as a base type.
2+
sharedInterfaceBaseTypes.ts(305,11): error TS2310: Type 'Cycle2' recursively references itself as a base type.
3+
sharedInterfaceBaseTypes.ts(306,11): error TS2310: Type 'Self' recursively references itself as a base type.
4+
5+
6+
==== sharedInterfaceBaseTypes.ts (3 errors) ====
7+
// https://github.com/microsoft/TypeScript/issues/63555
8+
9+
// Both interfaces at each level extend the previous level, so I40 reaches I0
10+
// through 2^40 paths. Checking each base type for circularity must not walk them all.
11+
12+
interface I0 { i0: string }
13+
interface A1 extends I0 { a1: string }
14+
interface B1 extends I0 { b1: string }
15+
interface I1 extends A1, B1 {}
16+
interface A2 extends I1 { a2: string }
17+
interface B2 extends I1 { b2: string }
18+
interface I2 extends A2, B2 {}
19+
interface A3 extends I2 { a3: string }
20+
interface B3 extends I2 { b3: string }
21+
interface I3 extends A3, B3 {}
22+
interface A4 extends I3 { a4: string }
23+
interface B4 extends I3 { b4: string }
24+
interface I4 extends A4, B4 {}
25+
interface A5 extends I4 { a5: string }
26+
interface B5 extends I4 { b5: string }
27+
interface I5 extends A5, B5 {}
28+
interface A6 extends I5 { a6: string }
29+
interface B6 extends I5 { b6: string }
30+
interface I6 extends A6, B6 {}
31+
interface A7 extends I6 { a7: string }
32+
interface B7 extends I6 { b7: string }
33+
interface I7 extends A7, B7 {}
34+
interface A8 extends I7 { a8: string }
35+
interface B8 extends I7 { b8: string }
36+
interface I8 extends A8, B8 {}
37+
interface A9 extends I8 { a9: string }
38+
interface B9 extends I8 { b9: string }
39+
interface I9 extends A9, B9 {}
40+
interface A10 extends I9 { a10: string }
41+
interface B10 extends I9 { b10: string }
42+
interface I10 extends A10, B10 {}
43+
interface A11 extends I10 { a11: string }
44+
interface B11 extends I10 { b11: string }
45+
interface I11 extends A11, B11 {}
46+
interface A12 extends I11 { a12: string }
47+
interface B12 extends I11 { b12: string }
48+
interface I12 extends A12, B12 {}
49+
interface A13 extends I12 { a13: string }
50+
interface B13 extends I12 { b13: string }
51+
interface I13 extends A13, B13 {}
52+
interface A14 extends I13 { a14: string }
53+
interface B14 extends I13 { b14: string }
54+
interface I14 extends A14, B14 {}
55+
interface A15 extends I14 { a15: string }
56+
interface B15 extends I14 { b15: string }
57+
interface I15 extends A15, B15 {}
58+
interface A16 extends I15 { a16: string }
59+
interface B16 extends I15 { b16: string }
60+
interface I16 extends A16, B16 {}
61+
interface A17 extends I16 { a17: string }
62+
interface B17 extends I16 { b17: string }
63+
interface I17 extends A17, B17 {}
64+
interface A18 extends I17 { a18: string }
65+
interface B18 extends I17 { b18: string }
66+
interface I18 extends A18, B18 {}
67+
interface A19 extends I18 { a19: string }
68+
interface B19 extends I18 { b19: string }
69+
interface I19 extends A19, B19 {}
70+
interface A20 extends I19 { a20: string }
71+
interface B20 extends I19 { b20: string }
72+
interface I20 extends A20, B20 {}
73+
interface A21 extends I20 { a21: string }
74+
interface B21 extends I20 { b21: string }
75+
interface I21 extends A21, B21 {}
76+
interface A22 extends I21 { a22: string }
77+
interface B22 extends I21 { b22: string }
78+
interface I22 extends A22, B22 {}
79+
interface A23 extends I22 { a23: string }
80+
interface B23 extends I22 { b23: string }
81+
interface I23 extends A23, B23 {}
82+
interface A24 extends I23 { a24: string }
83+
interface B24 extends I23 { b24: string }
84+
interface I24 extends A24, B24 {}
85+
interface A25 extends I24 { a25: string }
86+
interface B25 extends I24 { b25: string }
87+
interface I25 extends A25, B25 {}
88+
interface A26 extends I25 { a26: string }
89+
interface B26 extends I25 { b26: string }
90+
interface I26 extends A26, B26 {}
91+
interface A27 extends I26 { a27: string }
92+
interface B27 extends I26 { b27: string }
93+
interface I27 extends A27, B27 {}
94+
interface A28 extends I27 { a28: string }
95+
interface B28 extends I27 { b28: string }
96+
interface I28 extends A28, B28 {}
97+
interface A29 extends I28 { a29: string }
98+
interface B29 extends I28 { b29: string }
99+
interface I29 extends A29, B29 {}
100+
interface A30 extends I29 { a30: string }
101+
interface B30 extends I29 { b30: string }
102+
interface I30 extends A30, B30 {}
103+
interface A31 extends I30 { a31: string }
104+
interface B31 extends I30 { b31: string }
105+
interface I31 extends A31, B31 {}
106+
interface A32 extends I31 { a32: string }
107+
interface B32 extends I31 { b32: string }
108+
interface I32 extends A32, B32 {}
109+
interface A33 extends I32 { a33: string }
110+
interface B33 extends I32 { b33: string }
111+
interface I33 extends A33, B33 {}
112+
interface A34 extends I33 { a34: string }
113+
interface B34 extends I33 { b34: string }
114+
interface I34 extends A34, B34 {}
115+
interface A35 extends I34 { a35: string }
116+
interface B35 extends I34 { b35: string }
117+
interface I35 extends A35, B35 {}
118+
interface A36 extends I35 { a36: string }
119+
interface B36 extends I35 { b36: string }
120+
interface I36 extends A36, B36 {}
121+
interface A37 extends I36 { a37: string }
122+
interface B37 extends I36 { b37: string }
123+
interface I37 extends A37, B37 {}
124+
interface A38 extends I37 { a38: string }
125+
interface B38 extends I37 { b38: string }
126+
interface I38 extends A38, B38 {}
127+
interface A39 extends I38 { a39: string }
128+
interface B39 extends I38 { b39: string }
129+
interface I39 extends A39, B39 {}
130+
interface A40 extends I39 { a40: string }
131+
interface B40 extends I39 { b40: string }
132+
interface I40 extends A40, B40 {}
133+
134+
declare const viaInterfaces: I40;
135+
viaInterfaces.i0;
136+
137+
// The same graph through intersections.
138+
139+
interface J0 { j0: string }
140+
interface C1 extends J0 { c1: string }
141+
interface D1 extends J0 { d1: string }
142+
interface J1 extends Both1 {}
143+
type Both1 = C1 & D1;
144+
interface C2 extends J1 { c2: string }
145+
interface D2 extends J1 { d2: string }
146+
interface J2 extends Both2 {}
147+
type Both2 = C2 & D2;
148+
interface C3 extends J2 { c3: string }
149+
interface D3 extends J2 { d3: string }
150+
interface J3 extends Both3 {}
151+
type Both3 = C3 & D3;
152+
interface C4 extends J3 { c4: string }
153+
interface D4 extends J3 { d4: string }
154+
interface J4 extends Both4 {}
155+
type Both4 = C4 & D4;
156+
interface C5 extends J4 { c5: string }
157+
interface D5 extends J4 { d5: string }
158+
interface J5 extends Both5 {}
159+
type Both5 = C5 & D5;
160+
interface C6 extends J5 { c6: string }
161+
interface D6 extends J5 { d6: string }
162+
interface J6 extends Both6 {}
163+
type Both6 = C6 & D6;
164+
interface C7 extends J6 { c7: string }
165+
interface D7 extends J6 { d7: string }
166+
interface J7 extends Both7 {}
167+
type Both7 = C7 & D7;
168+
interface C8 extends J7 { c8: string }
169+
interface D8 extends J7 { d8: string }
170+
interface J8 extends Both8 {}
171+
type Both8 = C8 & D8;
172+
interface C9 extends J8 { c9: string }
173+
interface D9 extends J8 { d9: string }
174+
interface J9 extends Both9 {}
175+
type Both9 = C9 & D9;
176+
interface C10 extends J9 { c10: string }
177+
interface D10 extends J9 { d10: string }
178+
interface J10 extends Both10 {}
179+
type Both10 = C10 & D10;
180+
interface C11 extends J10 { c11: string }
181+
interface D11 extends J10 { d11: string }
182+
interface J11 extends Both11 {}
183+
type Both11 = C11 & D11;
184+
interface C12 extends J11 { c12: string }
185+
interface D12 extends J11 { d12: string }
186+
interface J12 extends Both12 {}
187+
type Both12 = C12 & D12;
188+
interface C13 extends J12 { c13: string }
189+
interface D13 extends J12 { d13: string }
190+
interface J13 extends Both13 {}
191+
type Both13 = C13 & D13;
192+
interface C14 extends J13 { c14: string }
193+
interface D14 extends J13 { d14: string }
194+
interface J14 extends Both14 {}
195+
type Both14 = C14 & D14;
196+
interface C15 extends J14 { c15: string }
197+
interface D15 extends J14 { d15: string }
198+
interface J15 extends Both15 {}
199+
type Both15 = C15 & D15;
200+
interface C16 extends J15 { c16: string }
201+
interface D16 extends J15 { d16: string }
202+
interface J16 extends Both16 {}
203+
type Both16 = C16 & D16;
204+
interface C17 extends J16 { c17: string }
205+
interface D17 extends J16 { d17: string }
206+
interface J17 extends Both17 {}
207+
type Both17 = C17 & D17;
208+
interface C18 extends J17 { c18: string }
209+
interface D18 extends J17 { d18: string }
210+
interface J18 extends Both18 {}
211+
type Both18 = C18 & D18;
212+
interface C19 extends J18 { c19: string }
213+
interface D19 extends J18 { d19: string }
214+
interface J19 extends Both19 {}
215+
type Both19 = C19 & D19;
216+
interface C20 extends J19 { c20: string }
217+
interface D20 extends J19 { d20: string }
218+
interface J20 extends Both20 {}
219+
type Both20 = C20 & D20;
220+
interface C21 extends J20 { c21: string }
221+
interface D21 extends J20 { d21: string }
222+
interface J21 extends Both21 {}
223+
type Both21 = C21 & D21;
224+
interface C22 extends J21 { c22: string }
225+
interface D22 extends J21 { d22: string }
226+
interface J22 extends Both22 {}
227+
type Both22 = C22 & D22;
228+
interface C23 extends J22 { c23: string }
229+
interface D23 extends J22 { d23: string }
230+
interface J23 extends Both23 {}
231+
type Both23 = C23 & D23;
232+
interface C24 extends J23 { c24: string }
233+
interface D24 extends J23 { d24: string }
234+
interface J24 extends Both24 {}
235+
type Both24 = C24 & D24;
236+
interface C25 extends J24 { c25: string }
237+
interface D25 extends J24 { d25: string }
238+
interface J25 extends Both25 {}
239+
type Both25 = C25 & D25;
240+
interface C26 extends J25 { c26: string }
241+
interface D26 extends J25 { d26: string }
242+
interface J26 extends Both26 {}
243+
type Both26 = C26 & D26;
244+
interface C27 extends J26 { c27: string }
245+
interface D27 extends J26 { d27: string }
246+
interface J27 extends Both27 {}
247+
type Both27 = C27 & D27;
248+
interface C28 extends J27 { c28: string }
249+
interface D28 extends J27 { d28: string }
250+
interface J28 extends Both28 {}
251+
type Both28 = C28 & D28;
252+
interface C29 extends J28 { c29: string }
253+
interface D29 extends J28 { d29: string }
254+
interface J29 extends Both29 {}
255+
type Both29 = C29 & D29;
256+
interface C30 extends J29 { c30: string }
257+
interface D30 extends J29 { d30: string }
258+
interface J30 extends Both30 {}
259+
type Both30 = C30 & D30;
260+
interface C31 extends J30 { c31: string }
261+
interface D31 extends J30 { d31: string }
262+
interface J31 extends Both31 {}
263+
type Both31 = C31 & D31;
264+
interface C32 extends J31 { c32: string }
265+
interface D32 extends J31 { d32: string }
266+
interface J32 extends Both32 {}
267+
type Both32 = C32 & D32;
268+
interface C33 extends J32 { c33: string }
269+
interface D33 extends J32 { d33: string }
270+
interface J33 extends Both33 {}
271+
type Both33 = C33 & D33;
272+
interface C34 extends J33 { c34: string }
273+
interface D34 extends J33 { d34: string }
274+
interface J34 extends Both34 {}
275+
type Both34 = C34 & D34;
276+
interface C35 extends J34 { c35: string }
277+
interface D35 extends J34 { d35: string }
278+
interface J35 extends Both35 {}
279+
type Both35 = C35 & D35;
280+
interface C36 extends J35 { c36: string }
281+
interface D36 extends J35 { d36: string }
282+
interface J36 extends Both36 {}
283+
type Both36 = C36 & D36;
284+
interface C37 extends J36 { c37: string }
285+
interface D37 extends J36 { d37: string }
286+
interface J37 extends Both37 {}
287+
type Both37 = C37 & D37;
288+
interface C38 extends J37 { c38: string }
289+
interface D38 extends J37 { d38: string }
290+
interface J38 extends Both38 {}
291+
type Both38 = C38 & D38;
292+
interface C39 extends J38 { c39: string }
293+
interface D39 extends J38 { d39: string }
294+
interface J39 extends Both39 {}
295+
type Both39 = C39 & D39;
296+
interface C40 extends J39 { c40: string }
297+
interface D40 extends J39 { d40: string }
298+
interface J40 extends Both40 {}
299+
type Both40 = C40 & D40;
300+
301+
declare const viaIntersections: J40;
302+
viaIntersections.j0;
303+
304+
// Cycles through shared base types are still reported.
305+
306+
interface Leaf { leaf: string }
307+
interface Left extends Leaf {}
308+
interface Right extends Leaf {}
309+
interface Shared extends Left, Right {}
310+
interface Cycle1 extends Shared, Cycle2 {}
311+
~~~~~~
312+
!!! error TS2310: Type 'Cycle1' recursively references itself as a base type.
313+
interface Cycle2 extends Left, Cycle1 {}
314+
~~~~~~
315+
!!! error TS2310: Type 'Cycle2' recursively references itself as a base type.
316+
interface Self extends Shared, Self {}
317+
~~~~
318+
!!! error TS2310: Type 'Self' recursively references itself as a base type.
319+
320+
// A default type argument that refers to the derived interface is not a cycle.
321+
322+
interface DefaultBase<T = DefaultDerived> extends Shared { next?: T }
323+
interface DefaultDerived extends DefaultBase {}
324+

0 commit comments

Comments
 (0)