diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e3a0fee79b3e7..626d7550416e4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2608,6 +2608,12 @@ namespace ts { const type = createType(TypeFlags.Object); type.objectFlags = objectFlags; type.symbol = symbol!; + type.members = undefined; + type.properties = undefined; + type.callSignatures = undefined; + type.constructSignatures = undefined; + type.stringIndexInfo = undefined; + type.numberIndexInfo = undefined; return type; } @@ -2629,11 +2635,8 @@ namespace ts { function getNamedMembers(members: SymbolTable): Symbol[] { let result: Symbol[] | undefined; members.forEach((symbol, id) => { - if (!isReservedMemberName(id)) { - if (!result) result = []; - if (symbolIsValue(symbol)) { - result.push(symbol); - } + if (!isReservedMemberName(id) && symbolIsValue(symbol)) { + (result || (result = [])).push(symbol); } }); return result || emptyArray; @@ -2641,11 +2644,11 @@ namespace ts { function setStructuredTypeMembers(type: StructuredType, members: SymbolTable, callSignatures: ReadonlyArray, constructSignatures: ReadonlyArray, stringIndexInfo: IndexInfo | undefined, numberIndexInfo: IndexInfo | undefined): ResolvedType { (type).members = members; - (type).properties = getNamedMembers(members); + (type).properties = members === emptySymbols ? emptyArray : getNamedMembers(members); (type).callSignatures = callSignatures; (type).constructSignatures = constructSignatures; - if (stringIndexInfo) (type).stringIndexInfo = stringIndexInfo; - if (numberIndexInfo) (type).numberIndexInfo = numberIndexInfo; + (type).stringIndexInfo = stringIndexInfo; + (type).numberIndexInfo = numberIndexInfo; return type; } @@ -5514,7 +5517,7 @@ namespace ts { // (otherwise there'd be an error from hasBaseType) - this is fine, but `.members` should be reset // as `getIndexedAccessType` via `instantiateType` via `getTypeFromClassOrInterfaceReference` forces a // partial instantiation of the members without the base types fully resolved - (type as Type as ResolvedType).members = undefined!; // TODO: GH#18217 + type.members = undefined; } return type.resolvedBaseTypes = [baseType]; } @@ -6444,6 +6447,7 @@ namespace ts { function resolveAnonymousTypeMembers(type: AnonymousType) { const symbol = type.symbol; if (type.target) { + setStructuredTypeMembers(type, emptySymbols, emptyArray, emptyArray, undefined, undefined); const members = createInstantiatedSymbolTable(getPropertiesOfObjectType(type.target), type.mapper!, /*mappingThisOnly*/ false); const callSignatures = instantiateSignatures(getSignaturesOfType(type.target, SignatureKind.Call), type.mapper!); const constructSignatures = instantiateSignatures(getSignaturesOfType(type.target, SignatureKind.Construct), type.mapper!); @@ -6452,6 +6456,7 @@ namespace ts { setStructuredTypeMembers(type, members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo); } else if (symbol.flags & SymbolFlags.TypeLiteral) { + setStructuredTypeMembers(type, emptySymbols, emptyArray, emptyArray, undefined, undefined); const members = getMembersOfSymbol(symbol); const callSignatures = getSignaturesOfSymbol(members.get(InternalSymbolName.Call)); const constructSignatures = getSignaturesOfSymbol(members.get(InternalSymbolName.New)); @@ -6485,7 +6490,7 @@ namespace ts { // in the process of resolving (see issue #6072). The temporarily empty signature list // will never be observed because a qualified name can't reference signatures. if (symbol.flags & (SymbolFlags.Function | SymbolFlags.Method)) { - (type).callSignatures = getSignaturesOfSymbol(symbol); + type.callSignatures = getSignaturesOfSymbol(symbol); } // And likewise for construct signatures for classes if (symbol.flags & SymbolFlags.Class) { @@ -6494,7 +6499,7 @@ namespace ts { if (!constructSignatures.length) { constructSignatures = getDefaultConstructSignatures(classType); } - (type).constructSignatures = constructSignatures; + type.constructSignatures = constructSignatures; } } } @@ -7674,7 +7679,7 @@ namespace ts { // will result in a different declaration kind. if (!signature.isolatedSignatureType) { const isConstructor = signature.declaration!.kind === SyntaxKind.Constructor || signature.declaration!.kind === SyntaxKind.ConstructSignature; // TODO: GH#18217 - const type = createObjectType(ObjectFlags.Anonymous); + const type = createObjectType(ObjectFlags.Anonymous); type.members = emptySymbols; type.properties = emptyArray; type.callSignatures = !isConstructor ? [signature] : emptyArray; @@ -10110,7 +10115,7 @@ namespace ts { if (type.flags & TypeFlags.Object) { const resolved = resolveStructuredTypeMembers(type); if (resolved.constructSignatures.length) { - const result = createObjectType(ObjectFlags.Anonymous, type.symbol); + const result = createObjectType(ObjectFlags.Anonymous, type.symbol); result.members = resolved.members; result.properties = resolved.properties; result.callSignatures = emptyArray; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 55e2b1d24adee..b038d524afc2f 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3820,6 +3820,12 @@ namespace ts { // Object types (TypeFlags.ObjectType) export interface ObjectType extends Type { objectFlags: ObjectFlags; + /* @internal */ members?: SymbolTable; // Properties by name + /* @internal */ properties?: Symbol[]; // Properties + /* @internal */ callSignatures?: ReadonlyArray; // Call signatures of type + /* @internal */ constructSignatures?: ReadonlyArray; // Construct signatures of type + /* @internal */ stringIndexInfo?: IndexInfo; // String indexing info + /* @internal */ numberIndexInfo?: IndexInfo; // Numeric indexing info } /** Class and interface types (ObjectFlags.Class and ObjectFlags.Interface). */ @@ -3946,8 +3952,6 @@ namespace ts { properties: Symbol[]; // Properties callSignatures: ReadonlyArray; // Call signatures of type constructSignatures: ReadonlyArray; // Construct signatures of type - stringIndexInfo?: IndexInfo; // String indexing info - numberIndexInfo?: IndexInfo; // Numeric indexing info } /* @internal */ diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 89214bdb96c2c..4ac18f0726aae 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -3312,6 +3312,12 @@ declare namespace ts { } interface ObjectType extends Type { objectFlags: ObjectFlags; + members?: SymbolTable; + properties?: Symbol[]; + callSignatures?: ReadonlyArray; + constructSignatures?: ReadonlyArray; + stringIndexInfo?: IndexInfo; + numberIndexInfo?: IndexInfo; } /** Class and interface types (ObjectFlags.Class and ObjectFlags.Interface). */ interface InterfaceType extends ObjectType { @@ -3402,8 +3408,6 @@ declare namespace ts { properties: Symbol[]; callSignatures: ReadonlyArray; constructSignatures: ReadonlyArray; - stringIndexInfo?: IndexInfo; - numberIndexInfo?: IndexInfo; } interface FreshObjectLiteralType extends ResolvedType { regularType: ResolvedType; diff --git a/tests/baselines/reference/recursiveResolveTypeMembers.errors.txt b/tests/baselines/reference/recursiveResolveTypeMembers.errors.txt new file mode 100644 index 0000000000000..2a6bab18cbaac --- /dev/null +++ b/tests/baselines/reference/recursiveResolveTypeMembers.errors.txt @@ -0,0 +1,19 @@ +tests/cases/compiler/recursiveResolveTypeMembers.ts(4,58): error TS2304: Cannot find name 'H'. +tests/cases/compiler/recursiveResolveTypeMembers.ts(4,62): error TS2574: A rest element type must be an array type. +tests/cases/compiler/recursiveResolveTypeMembers.ts(4,79): error TS2304: Cannot find name 'R'. + + +==== tests/cases/compiler/recursiveResolveTypeMembers.ts (3 errors) ==== + // Repro from #25291 + + type PromisedTuple void> = + U extends (h: infer H, ...args: infer R) => [Promise, ...PromisedTuple] ? [] : [] + ~ +!!! error TS2304: Cannot find name 'H'. + ~~~~~~~~~~~~~~~~~~~ +!!! error TS2574: A rest element type must be an array type. + ~ +!!! error TS2304: Cannot find name 'R'. + + type Promised = PromisedTuple<[1, 2, 3]> + \ No newline at end of file diff --git a/tests/baselines/reference/recursiveResolveTypeMembers.js b/tests/baselines/reference/recursiveResolveTypeMembers.js new file mode 100644 index 0000000000000..4e140bb3ba049 --- /dev/null +++ b/tests/baselines/reference/recursiveResolveTypeMembers.js @@ -0,0 +1,11 @@ +//// [recursiveResolveTypeMembers.ts] +// Repro from #25291 + +type PromisedTuple void> = + U extends (h: infer H, ...args: infer R) => [Promise, ...PromisedTuple] ? [] : [] + +type Promised = PromisedTuple<[1, 2, 3]> + + +//// [recursiveResolveTypeMembers.js] +// Repro from #25291 diff --git a/tests/baselines/reference/recursiveResolveTypeMembers.symbols b/tests/baselines/reference/recursiveResolveTypeMembers.symbols new file mode 100644 index 0000000000000..8e5fb0e982d60 --- /dev/null +++ b/tests/baselines/reference/recursiveResolveTypeMembers.symbols @@ -0,0 +1,23 @@ +=== tests/cases/compiler/recursiveResolveTypeMembers.ts === +// Repro from #25291 + +type PromisedTuple void> = +>PromisedTuple : Symbol(PromisedTuple, Decl(recursiveResolveTypeMembers.ts, 0, 0)) +>L : Symbol(L, Decl(recursiveResolveTypeMembers.ts, 2, 19)) +>U : Symbol(U, Decl(recursiveResolveTypeMembers.ts, 2, 35)) +>args : Symbol(args, Decl(recursiveResolveTypeMembers.ts, 2, 41)) +>L : Symbol(L, Decl(recursiveResolveTypeMembers.ts, 2, 19)) + + U extends (h: infer H, ...args: infer R) => [Promise, ...PromisedTuple] ? [] : [] +>U : Symbol(U, Decl(recursiveResolveTypeMembers.ts, 2, 35)) +>h : Symbol(h, Decl(recursiveResolveTypeMembers.ts, 3, 15)) +>H : Symbol(H, Decl(recursiveResolveTypeMembers.ts, 3, 23)) +>args : Symbol(args, Decl(recursiveResolveTypeMembers.ts, 3, 26)) +>R : Symbol(R, Decl(recursiveResolveTypeMembers.ts, 3, 41)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --)) +>PromisedTuple : Symbol(PromisedTuple, Decl(recursiveResolveTypeMembers.ts, 0, 0)) + +type Promised = PromisedTuple<[1, 2, 3]> +>Promised : Symbol(Promised, Decl(recursiveResolveTypeMembers.ts, 3, 91)) +>PromisedTuple : Symbol(PromisedTuple, Decl(recursiveResolveTypeMembers.ts, 0, 0)) + diff --git a/tests/baselines/reference/recursiveResolveTypeMembers.types b/tests/baselines/reference/recursiveResolveTypeMembers.types new file mode 100644 index 0000000000000..5ca57a116c46a --- /dev/null +++ b/tests/baselines/reference/recursiveResolveTypeMembers.types @@ -0,0 +1,25 @@ +=== tests/cases/compiler/recursiveResolveTypeMembers.ts === +// Repro from #25291 + +type PromisedTuple void> = +>PromisedTuple : PromisedTuple +>L : L +>U : U +>args : L +>L : L + + U extends (h: infer H, ...args: infer R) => [Promise, ...PromisedTuple] ? [] : [] +>U : U +>h : H +>H : H +>args : R +>R : R +>Promise : Promise +>H : No type information available! +>PromisedTuple : PromisedTuple +>R : No type information available! + +type Promised = PromisedTuple<[1, 2, 3]> +>Promised : [] +>PromisedTuple : PromisedTuple + diff --git a/tests/cases/compiler/recursiveResolveTypeMembers.ts b/tests/cases/compiler/recursiveResolveTypeMembers.ts new file mode 100644 index 0000000000000..5c7920e43fb20 --- /dev/null +++ b/tests/cases/compiler/recursiveResolveTypeMembers.ts @@ -0,0 +1,6 @@ +// Repro from #25291 + +type PromisedTuple void> = + U extends (h: infer H, ...args: infer R) => [Promise, ...PromisedTuple] ? [] : [] + +type Promised = PromisedTuple<[1, 2, 3]>