From d088733b24701cfae9719ee4b9b773fd112fec70 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Thu, 16 Nov 2023 16:44:24 -0800 Subject: [PATCH] Produce wide index signatures from the spread of an index signature --- src/compiler/checker.ts | 28 +++++++++++++-- .../reference/spreadIndexSignature.js | 15 ++++++++ .../reference/spreadIndexSignature.symbols | 32 +++++++++++++++++ .../reference/spreadIndexSignature.types | 36 +++++++++++++++++++ tests/cases/compiler/spreadIndexSignature.ts | 8 +++++ 5 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/spreadIndexSignature.js create mode 100644 tests/baselines/reference/spreadIndexSignature.symbols create mode 100644 tests/baselines/reference/spreadIndexSignature.types create mode 100644 tests/cases/compiler/spreadIndexSignature.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f27a6058006..f13f604ddcd 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13435,6 +13435,26 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return emptyArray; } + function getSpreadIndexInfos(left: Type, right: Type): IndexInfo[] { + const rightInfos = getIndexInfosOfType(right); + if (!rightInfos.length) return emptyArray; + const leftInfos = getIndexInfosOfType(left); + const leftProperties = getPropertiesOfType(left); + const result: IndexInfo[] = []; + for (const rightInfo of rightInfos) { + const indexType = rightInfo.keyType; + const leftInfo = findIndexInfo(leftInfos, indexType); + const types = leftInfo ? [leftInfo.type, rightInfo.type] : [rightInfo.type]; + for (const prop of leftProperties) { + if (isApplicableIndexType(getNameTypeOfPropertyName(prop.escapedName), indexType)) { + types.push(getTypeOfSymbol(prop)); + } + } + result.push(createIndexInfo(indexType, getUnionType(types), leftInfo && leftInfo.isReadonly || rightInfo.isReadonly)); + } + return result; + } + function resolveUnionTypeMembers(type: UnionType) { // The members and properties collections are empty for union types. To get all properties of a union // type use getPropertiesOfType (only the language service uses this). @@ -14888,7 +14908,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function getApplicableIndexInfoForName(type: Type, name: __String): IndexInfo | undefined { - return getApplicableIndexInfo(type, isLateBoundName(name) ? esSymbolType : getStringLiteralType(unescapeLeadingUnderscores(name))); + return getApplicableIndexInfo(type, getNameTypeOfPropertyName(name)); + } + + function getNameTypeOfPropertyName(name: __String): Type { + return isLateBoundName(name) ? esSymbolType : getStringLiteralType(unescapeLeadingUnderscores(name)); } // Return list of type parameters with duplicates removed (duplicate identifier errors are generated in the actual @@ -18793,7 +18817,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const members = createSymbolTable(); const skippedPrivateMembers = new Set<__String>(); - const indexInfos = left === emptyObjectType ? getIndexInfosOfType(right) : getUnionIndexInfos([left, right]); + const indexInfos = left === emptyObjectType ? getIndexInfosOfType(right) : getSpreadIndexInfos(left, right); for (const rightProp of getPropertiesOfType(right)) { if (getDeclarationModifierFlagsFromSymbol(rightProp) & (ModifierFlags.Private | ModifierFlags.Protected)) { diff --git a/tests/baselines/reference/spreadIndexSignature.js b/tests/baselines/reference/spreadIndexSignature.js new file mode 100644 index 00000000000..4963257e008 --- /dev/null +++ b/tests/baselines/reference/spreadIndexSignature.js @@ -0,0 +1,15 @@ +//// [tests/cases/compiler/spreadIndexSignature.ts] //// + +//// [spreadIndexSignature.ts] +declare const strings: Record; +declare const symbols: Record; + +const o1 = { a: 1, ...strings }; +const o2 = { [Symbol.iterator]: 1, ...strings }; +const o3 = { [Symbol.iterator]: 1, ...symbols }; + + +//// [spreadIndexSignature.js] +const o1 = { a: 1, ...strings }; +const o2 = { [Symbol.iterator]: 1, ...strings }; +const o3 = { [Symbol.iterator]: 1, ...symbols }; diff --git a/tests/baselines/reference/spreadIndexSignature.symbols b/tests/baselines/reference/spreadIndexSignature.symbols new file mode 100644 index 00000000000..9811f1f123b --- /dev/null +++ b/tests/baselines/reference/spreadIndexSignature.symbols @@ -0,0 +1,32 @@ +//// [tests/cases/compiler/spreadIndexSignature.ts] //// + +=== spreadIndexSignature.ts === +declare const strings: Record; +>strings : Symbol(strings, Decl(spreadIndexSignature.ts, 0, 13)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) + +declare const symbols: Record; +>symbols : Symbol(symbols, Decl(spreadIndexSignature.ts, 1, 13)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) + +const o1 = { a: 1, ...strings }; +>o1 : Symbol(o1, Decl(spreadIndexSignature.ts, 3, 5)) +>a : Symbol(a, Decl(spreadIndexSignature.ts, 3, 12)) +>strings : Symbol(strings, Decl(spreadIndexSignature.ts, 0, 13)) + +const o2 = { [Symbol.iterator]: 1, ...strings }; +>o2 : Symbol(o2, Decl(spreadIndexSignature.ts, 4, 5)) +>[Symbol.iterator] : Symbol([Symbol.iterator], Decl(spreadIndexSignature.ts, 4, 12)) +>Symbol.iterator : Symbol(SymbolConstructor.iterator, Decl(lib.es2015.iterable.d.ts, --, --)) +>Symbol : Symbol(Symbol, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2019.symbol.d.ts, --, --)) +>iterator : Symbol(SymbolConstructor.iterator, Decl(lib.es2015.iterable.d.ts, --, --)) +>strings : Symbol(strings, Decl(spreadIndexSignature.ts, 0, 13)) + +const o3 = { [Symbol.iterator]: 1, ...symbols }; +>o3 : Symbol(o3, Decl(spreadIndexSignature.ts, 5, 5)) +>[Symbol.iterator] : Symbol([Symbol.iterator], Decl(spreadIndexSignature.ts, 5, 12)) +>Symbol.iterator : Symbol(SymbolConstructor.iterator, Decl(lib.es2015.iterable.d.ts, --, --)) +>Symbol : Symbol(Symbol, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2019.symbol.d.ts, --, --)) +>iterator : Symbol(SymbolConstructor.iterator, Decl(lib.es2015.iterable.d.ts, --, --)) +>symbols : Symbol(symbols, Decl(spreadIndexSignature.ts, 1, 13)) + diff --git a/tests/baselines/reference/spreadIndexSignature.types b/tests/baselines/reference/spreadIndexSignature.types new file mode 100644 index 00000000000..cd616ab3193 --- /dev/null +++ b/tests/baselines/reference/spreadIndexSignature.types @@ -0,0 +1,36 @@ +//// [tests/cases/compiler/spreadIndexSignature.ts] //// + +=== spreadIndexSignature.ts === +declare const strings: Record; +>strings : Record + +declare const symbols: Record; +>symbols : Record + +const o1 = { a: 1, ...strings }; +>o1 : { [x: string]: string | number; a: number; } +>{ a: 1, ...strings } : { [x: string]: string | number; a: number; } +>a : number +>1 : 1 +>strings : Record + +const o2 = { [Symbol.iterator]: 1, ...strings }; +>o2 : { [x: string]: string; [Symbol.iterator]: number; } +>{ [Symbol.iterator]: 1, ...strings } : { [x: string]: string; [Symbol.iterator]: number; } +>[Symbol.iterator] : number +>Symbol.iterator : unique symbol +>Symbol : SymbolConstructor +>iterator : unique symbol +>1 : 1 +>strings : Record + +const o3 = { [Symbol.iterator]: 1, ...symbols }; +>o3 : { [x: symbol]: string | number; [Symbol.iterator]: number; } +>{ [Symbol.iterator]: 1, ...symbols } : { [x: symbol]: string | number; [Symbol.iterator]: number; } +>[Symbol.iterator] : number +>Symbol.iterator : unique symbol +>Symbol : SymbolConstructor +>iterator : unique symbol +>1 : 1 +>symbols : Record + diff --git a/tests/cases/compiler/spreadIndexSignature.ts b/tests/cases/compiler/spreadIndexSignature.ts new file mode 100644 index 00000000000..75215d6942e --- /dev/null +++ b/tests/cases/compiler/spreadIndexSignature.ts @@ -0,0 +1,8 @@ +// @target: esnext + +declare const strings: Record; +declare const symbols: Record; + +const o1 = { a: 1, ...strings }; +const o2 = { [Symbol.iterator]: 1, ...strings }; +const o3 = { [Symbol.iterator]: 1, ...symbols };