diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f27a6058006e5..f13f604ddcd2b 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/constAssertions.js b/tests/baselines/reference/constAssertions.js index 3173e2bf92ae3..8da3b3e5b51d0 100644 --- a/tests/baselines/reference/constAssertions.js +++ b/tests/baselines/reference/constAssertions.js @@ -258,6 +258,7 @@ declare let o2: { readonly d: () => void; }; declare let o3: { + readonly [x: string]: 10 | 1 | 2 | 3 | 20 | (() => void) | 4; readonly a: 1; readonly b: 2; readonly c: 3; diff --git a/tests/baselines/reference/constAssertions.types b/tests/baselines/reference/constAssertions.types index 0668c0235aa0e..a042eb1746686 100644 --- a/tests/baselines/reference/constAssertions.types +++ b/tests/baselines/reference/constAssertions.types @@ -202,9 +202,9 @@ let o2 = { a: 1, 'b': 2, ['c']: 3, d() {}, ['e' + '']: 4 } as const; >4 : 4 let o3 = { ...o1, ...o2 } as const; ->o3 : { readonly a: 1; readonly b: 2; readonly c: 3; readonly d: () => void; readonly x: 10; readonly y: 20; } ->{ ...o1, ...o2 } as const : { readonly a: 1; readonly b: 2; readonly c: 3; readonly d: () => void; readonly x: 10; readonly y: 20; } ->{ ...o1, ...o2 } : { readonly a: 1; readonly b: 2; readonly c: 3; readonly d: () => void; readonly x: 10; readonly y: 20; } +>o3 : { readonly [x: string]: 10 | 1 | 2 | 3 | 20 | (() => void) | 4; readonly a: 1; readonly b: 2; readonly c: 3; readonly d: () => void; readonly x: 10; readonly y: 20; } +>{ ...o1, ...o2 } as const : { readonly [x: string]: 10 | 1 | 2 | 3 | 20 | (() => void) | 4; readonly a: 1; readonly b: 2; readonly c: 3; readonly d: () => void; readonly x: 10; readonly y: 20; } +>{ ...o1, ...o2 } : { readonly [x: string]: 10 | 1 | 2 | 3 | 20 | (() => void) | 4; readonly a: 1; readonly b: 2; readonly c: 3; readonly d: () => void; readonly x: 10; readonly y: 20; } >o1 : { readonly x: 10; readonly y: 20; } >o2 : { readonly [x: string]: 1 | 2 | 3 | (() => void) | 4; readonly a: 1; readonly b: 2; readonly c: 3; readonly d: () => void; } diff --git a/tests/baselines/reference/objectSpreadComputedProperty.types b/tests/baselines/reference/objectSpreadComputedProperty.types index ce61004aa04c6..16bd8a6de0a68 100644 --- a/tests/baselines/reference/objectSpreadComputedProperty.types +++ b/tests/baselines/reference/objectSpreadComputedProperty.types @@ -17,24 +17,24 @@ function f() { >a : any const o1 = { ...{}, [n]: n }; ->o1 : {} ->{ ...{}, [n]: n } : {} +>o1 : { [x: number]: number; } +>{ ...{}, [n]: n } : { [x: number]: number; } >{} : {} >[n] : number >n : number >n : number const o2 = { ...{}, [a]: n }; ->o2 : {} ->{ ...{}, [a]: n } : {} +>o2 : { [x: number]: number; } +>{ ...{}, [a]: n } : { [x: number]: number; } >{} : {} >[a] : number >a : any >n : number const o3 = { [a]: n, ...{}, [n]: n, ...{}, [m]: m }; ->o3 : {} ->{ [a]: n, ...{}, [n]: n, ...{}, [m]: m } : {} +>o3 : { [x: number]: number; } +>{ [a]: n, ...{}, [n]: n, ...{}, [m]: m } : { [x: number]: number; } >[a] : number >a : any >n : number diff --git a/tests/baselines/reference/spreadIndexSignature.js b/tests/baselines/reference/spreadIndexSignature.js new file mode 100644 index 0000000000000..4963257e00840 --- /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 0000000000000..9811f1f123bf5 --- /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 0000000000000..cd616ab319354 --- /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/baselines/reference/useBeforeDeclaration_propertyAssignment.types b/tests/baselines/reference/useBeforeDeclaration_propertyAssignment.types index a6c6e1faceb89..15079f94f2c24 100644 --- a/tests/baselines/reference/useBeforeDeclaration_propertyAssignment.types +++ b/tests/baselines/reference/useBeforeDeclaration_propertyAssignment.types @@ -5,8 +5,8 @@ export class C { >C : C public a = { b: this.b, ...this.c, [this.b]: `${this.c}`}; ->a : { c: number; b: number; } ->{ b: this.b, ...this.c, [this.b]: `${this.c}`} : { c: number; b: number; } +>a : { [x: number]: string; c: number; b: number; } +>{ b: this.b, ...this.c, [this.b]: `${this.c}`} : { [x: number]: string; c: number; b: number; } >b : number >this.b : number >this : this diff --git a/tests/cases/compiler/spreadIndexSignature.ts b/tests/cases/compiler/spreadIndexSignature.ts new file mode 100644 index 0000000000000..75215d6942ef7 --- /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 };