Skip to content

Commit e8d64a9

Browse files
authored
Merge pull request #25336 from Microsoft/fixResolveTypeMembers
Fix runaway recursion in object type member resolution
2 parents 3edf8ed + b87a723 commit e8d64a9

8 files changed

+114
-17
lines changed

src/compiler/checker.ts

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2542,6 +2542,12 @@ namespace ts {
25422542
const type = <ObjectType>createType(TypeFlags.Object);
25432543
type.objectFlags = objectFlags;
25442544
type.symbol = symbol!;
2545+
type.members = undefined;
2546+
type.properties = undefined;
2547+
type.callSignatures = undefined;
2548+
type.constructSignatures = undefined;
2549+
type.stringIndexInfo = undefined;
2550+
type.numberIndexInfo = undefined;
25452551
return type;
25462552
}
25472553

@@ -2563,23 +2569,20 @@ namespace ts {
25632569
function getNamedMembers(members: SymbolTable): Symbol[] {
25642570
let result: Symbol[] | undefined;
25652571
members.forEach((symbol, id) => {
2566-
if (!isReservedMemberName(id)) {
2567-
if (!result) result = [];
2568-
if (symbolIsValue(symbol)) {
2569-
result.push(symbol);
2570-
}
2572+
if (!isReservedMemberName(id) && symbolIsValue(symbol)) {
2573+
(result || (result = [])).push(symbol);
25712574
}
25722575
});
25732576
return result || emptyArray;
25742577
}
25752578

25762579
function setStructuredTypeMembers(type: StructuredType, members: SymbolTable, callSignatures: ReadonlyArray<Signature>, constructSignatures: ReadonlyArray<Signature>, stringIndexInfo: IndexInfo | undefined, numberIndexInfo: IndexInfo | undefined): ResolvedType {
25772580
(<ResolvedType>type).members = members;
2578-
(<ResolvedType>type).properties = getNamedMembers(members);
2581+
(<ResolvedType>type).properties = members === emptySymbols ? emptyArray : getNamedMembers(members);
25792582
(<ResolvedType>type).callSignatures = callSignatures;
25802583
(<ResolvedType>type).constructSignatures = constructSignatures;
2581-
if (stringIndexInfo) (<ResolvedType>type).stringIndexInfo = stringIndexInfo;
2582-
if (numberIndexInfo) (<ResolvedType>type).numberIndexInfo = numberIndexInfo;
2584+
(<ResolvedType>type).stringIndexInfo = stringIndexInfo;
2585+
(<ResolvedType>type).numberIndexInfo = numberIndexInfo;
25832586
return <ResolvedType>type;
25842587
}
25852588

@@ -5450,7 +5453,7 @@ namespace ts {
54505453
// (otherwise there'd be an error from hasBaseType) - this is fine, but `.members` should be reset
54515454
// as `getIndexedAccessType` via `instantiateType` via `getTypeFromClassOrInterfaceReference` forces a
54525455
// partial instantiation of the members without the base types fully resolved
5453-
(type as Type as ResolvedType).members = undefined!; // TODO: GH#18217
5456+
type.members = undefined;
54545457
}
54555458
return type.resolvedBaseTypes = [baseType];
54565459
}
@@ -6380,6 +6383,7 @@ namespace ts {
63806383
function resolveAnonymousTypeMembers(type: AnonymousType) {
63816384
const symbol = type.symbol;
63826385
if (type.target) {
6386+
setStructuredTypeMembers(type, emptySymbols, emptyArray, emptyArray, undefined, undefined);
63836387
const members = createInstantiatedSymbolTable(getPropertiesOfObjectType(type.target), type.mapper!, /*mappingThisOnly*/ false);
63846388
const callSignatures = instantiateSignatures(getSignaturesOfType(type.target, SignatureKind.Call), type.mapper!);
63856389
const constructSignatures = instantiateSignatures(getSignaturesOfType(type.target, SignatureKind.Construct), type.mapper!);
@@ -6388,6 +6392,7 @@ namespace ts {
63886392
setStructuredTypeMembers(type, members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo);
63896393
}
63906394
else if (symbol.flags & SymbolFlags.TypeLiteral) {
6395+
setStructuredTypeMembers(type, emptySymbols, emptyArray, emptyArray, undefined, undefined);
63916396
const members = getMembersOfSymbol(symbol);
63926397
const callSignatures = getSignaturesOfSymbol(members.get(InternalSymbolName.Call));
63936398
const constructSignatures = getSignaturesOfSymbol(members.get(InternalSymbolName.New));
@@ -6421,7 +6426,7 @@ namespace ts {
64216426
// in the process of resolving (see issue #6072). The temporarily empty signature list
64226427
// will never be observed because a qualified name can't reference signatures.
64236428
if (symbol.flags & (SymbolFlags.Function | SymbolFlags.Method)) {
6424-
(<ResolvedType>type).callSignatures = getSignaturesOfSymbol(symbol);
6429+
type.callSignatures = getSignaturesOfSymbol(symbol);
64256430
}
64266431
// And likewise for construct signatures for classes
64276432
if (symbol.flags & SymbolFlags.Class) {
@@ -6430,7 +6435,7 @@ namespace ts {
64306435
if (!constructSignatures.length) {
64316436
constructSignatures = getDefaultConstructSignatures(classType);
64326437
}
6433-
(<ResolvedType>type).constructSignatures = constructSignatures;
6438+
type.constructSignatures = constructSignatures;
64346439
}
64356440
}
64366441
}
@@ -7610,7 +7615,7 @@ namespace ts {
76107615
// will result in a different declaration kind.
76117616
if (!signature.isolatedSignatureType) {
76127617
const isConstructor = signature.declaration!.kind === SyntaxKind.Constructor || signature.declaration!.kind === SyntaxKind.ConstructSignature; // TODO: GH#18217
7613-
const type = <ResolvedType>createObjectType(ObjectFlags.Anonymous);
7618+
const type = createObjectType(ObjectFlags.Anonymous);
76147619
type.members = emptySymbols;
76157620
type.properties = emptyArray;
76167621
type.callSignatures = !isConstructor ? [signature] : emptyArray;
@@ -10046,7 +10051,7 @@ namespace ts {
1004610051
if (type.flags & TypeFlags.Object) {
1004710052
const resolved = resolveStructuredTypeMembers(<ObjectType>type);
1004810053
if (resolved.constructSignatures.length) {
10049-
const result = <ResolvedType>createObjectType(ObjectFlags.Anonymous, type.symbol);
10054+
const result = createObjectType(ObjectFlags.Anonymous, type.symbol);
1005010055
result.members = resolved.members;
1005110056
result.properties = resolved.properties;
1005210057
result.callSignatures = emptyArray;

src/compiler/types.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3802,6 +3802,12 @@ namespace ts {
38023802
// Object types (TypeFlags.ObjectType)
38033803
export interface ObjectType extends Type {
38043804
objectFlags: ObjectFlags;
3805+
/* @internal */ members?: SymbolTable; // Properties by name
3806+
/* @internal */ properties?: Symbol[]; // Properties
3807+
/* @internal */ callSignatures?: ReadonlyArray<Signature>; // Call signatures of type
3808+
/* @internal */ constructSignatures?: ReadonlyArray<Signature>; // Construct signatures of type
3809+
/* @internal */ stringIndexInfo?: IndexInfo; // String indexing info
3810+
/* @internal */ numberIndexInfo?: IndexInfo; // Numeric indexing info
38053811
}
38063812

38073813
/** Class and interface types (ObjectFlags.Class and ObjectFlags.Interface). */
@@ -3928,8 +3934,6 @@ namespace ts {
39283934
properties: Symbol[]; // Properties
39293935
callSignatures: ReadonlyArray<Signature>; // Call signatures of type
39303936
constructSignatures: ReadonlyArray<Signature>; // Construct signatures of type
3931-
stringIndexInfo?: IndexInfo; // String indexing info
3932-
numberIndexInfo?: IndexInfo; // Numeric indexing info
39333937
}
39343938

39353939
/* @internal */

tests/baselines/reference/api/tsserverlibrary.d.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3292,6 +3292,12 @@ declare namespace ts {
32923292
}
32933293
interface ObjectType extends Type {
32943294
objectFlags: ObjectFlags;
3295+
members?: SymbolTable;
3296+
properties?: Symbol[];
3297+
callSignatures?: ReadonlyArray<Signature>;
3298+
constructSignatures?: ReadonlyArray<Signature>;
3299+
stringIndexInfo?: IndexInfo;
3300+
numberIndexInfo?: IndexInfo;
32953301
}
32963302
/** Class and interface types (ObjectFlags.Class and ObjectFlags.Interface). */
32973303
interface InterfaceType extends ObjectType {
@@ -3382,8 +3388,6 @@ declare namespace ts {
33823388
properties: Symbol[];
33833389
callSignatures: ReadonlyArray<Signature>;
33843390
constructSignatures: ReadonlyArray<Signature>;
3385-
stringIndexInfo?: IndexInfo;
3386-
numberIndexInfo?: IndexInfo;
33873391
}
33883392
interface FreshObjectLiteralType extends ResolvedType {
33893393
regularType: ResolvedType;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
tests/cases/compiler/recursiveResolveTypeMembers.ts(4,58): error TS2304: Cannot find name 'H'.
2+
tests/cases/compiler/recursiveResolveTypeMembers.ts(4,62): error TS2574: A rest element type must be an array type.
3+
tests/cases/compiler/recursiveResolveTypeMembers.ts(4,79): error TS2304: Cannot find name 'R'.
4+
5+
6+
==== tests/cases/compiler/recursiveResolveTypeMembers.ts (3 errors) ====
7+
// Repro from #25291
8+
9+
type PromisedTuple<L extends any[], U = (...args: L) => void> =
10+
U extends (h: infer H, ...args: infer R) => [Promise<H>, ...PromisedTuple<R>] ? [] : []
11+
~
12+
!!! error TS2304: Cannot find name 'H'.
13+
~~~~~~~~~~~~~~~~~~~
14+
!!! error TS2574: A rest element type must be an array type.
15+
~
16+
!!! error TS2304: Cannot find name 'R'.
17+
18+
type Promised = PromisedTuple<[1, 2, 3]>
19+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
//// [recursiveResolveTypeMembers.ts]
2+
// Repro from #25291
3+
4+
type PromisedTuple<L extends any[], U = (...args: L) => void> =
5+
U extends (h: infer H, ...args: infer R) => [Promise<H>, ...PromisedTuple<R>] ? [] : []
6+
7+
type Promised = PromisedTuple<[1, 2, 3]>
8+
9+
10+
//// [recursiveResolveTypeMembers.js]
11+
// Repro from #25291
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
=== tests/cases/compiler/recursiveResolveTypeMembers.ts ===
2+
// Repro from #25291
3+
4+
type PromisedTuple<L extends any[], U = (...args: L) => void> =
5+
>PromisedTuple : Symbol(PromisedTuple, Decl(recursiveResolveTypeMembers.ts, 0, 0))
6+
>L : Symbol(L, Decl(recursiveResolveTypeMembers.ts, 2, 19))
7+
>U : Symbol(U, Decl(recursiveResolveTypeMembers.ts, 2, 35))
8+
>args : Symbol(args, Decl(recursiveResolveTypeMembers.ts, 2, 41))
9+
>L : Symbol(L, Decl(recursiveResolveTypeMembers.ts, 2, 19))
10+
11+
U extends (h: infer H, ...args: infer R) => [Promise<H>, ...PromisedTuple<R>] ? [] : []
12+
>U : Symbol(U, Decl(recursiveResolveTypeMembers.ts, 2, 35))
13+
>h : Symbol(h, Decl(recursiveResolveTypeMembers.ts, 3, 15))
14+
>H : Symbol(H, Decl(recursiveResolveTypeMembers.ts, 3, 23))
15+
>args : Symbol(args, Decl(recursiveResolveTypeMembers.ts, 3, 26))
16+
>R : Symbol(R, Decl(recursiveResolveTypeMembers.ts, 3, 41))
17+
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --))
18+
>PromisedTuple : Symbol(PromisedTuple, Decl(recursiveResolveTypeMembers.ts, 0, 0))
19+
20+
type Promised = PromisedTuple<[1, 2, 3]>
21+
>Promised : Symbol(Promised, Decl(recursiveResolveTypeMembers.ts, 3, 91))
22+
>PromisedTuple : Symbol(PromisedTuple, Decl(recursiveResolveTypeMembers.ts, 0, 0))
23+
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
=== tests/cases/compiler/recursiveResolveTypeMembers.ts ===
2+
// Repro from #25291
3+
4+
type PromisedTuple<L extends any[], U = (...args: L) => void> =
5+
>PromisedTuple : PromisedTuple<L, U>
6+
>L : L
7+
>U : U
8+
>args : L
9+
>L : L
10+
11+
U extends (h: infer H, ...args: infer R) => [Promise<H>, ...PromisedTuple<R>] ? [] : []
12+
>U : U
13+
>h : H
14+
>H : H
15+
>args : R
16+
>R : R
17+
>Promise : Promise<T>
18+
>H : No type information available!
19+
>PromisedTuple : PromisedTuple<L, U>
20+
>R : No type information available!
21+
22+
type Promised = PromisedTuple<[1, 2, 3]>
23+
>Promised : []
24+
>PromisedTuple : PromisedTuple<L, U>
25+
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// Repro from #25291
2+
3+
type PromisedTuple<L extends any[], U = (...args: L) => void> =
4+
U extends (h: infer H, ...args: infer R) => [Promise<H>, ...PromisedTuple<R>] ? [] : []
5+
6+
type Promised = PromisedTuple<[1, 2, 3]>

0 commit comments

Comments
 (0)