Skip to content

Experiment: Produce wide index signatures from the spread of an index signature #56441

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)) {
Expand Down
1 change: 1 addition & 0 deletions tests/baselines/reference/constAssertions.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
6 changes: 3 additions & 3 deletions tests/baselines/reference/constAssertions.types
Original file line number Diff line number Diff line change
Expand Up @@ -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; }

Expand Down
12 changes: 6 additions & 6 deletions tests/baselines/reference/objectSpreadComputedProperty.types
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
15 changes: 15 additions & 0 deletions tests/baselines/reference/spreadIndexSignature.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//// [tests/cases/compiler/spreadIndexSignature.ts] ////

//// [spreadIndexSignature.ts]
declare const strings: Record<string, string>;
declare const symbols: Record<symbol, string>;

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 };
32 changes: 32 additions & 0 deletions tests/baselines/reference/spreadIndexSignature.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//// [tests/cases/compiler/spreadIndexSignature.ts] ////

=== spreadIndexSignature.ts ===
declare const strings: Record<string, string>;
>strings : Symbol(strings, Decl(spreadIndexSignature.ts, 0, 13))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))

declare const symbols: Record<symbol, string>;
>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))

36 changes: 36 additions & 0 deletions tests/baselines/reference/spreadIndexSignature.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//// [tests/cases/compiler/spreadIndexSignature.ts] ////

=== spreadIndexSignature.ts ===
declare const strings: Record<string, string>;
>strings : Record<string, string>

declare const symbols: Record<symbol, string>;
>symbols : Record<symbol, string>

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<string, string>

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<string, string>

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<symbol, string>

Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions tests/cases/compiler/spreadIndexSignature.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// @target: esnext

declare const strings: Record<string, string>;
declare const symbols: Record<symbol, string>;

const o1 = { a: 1, ...strings };
const o2 = { [Symbol.iterator]: 1, ...strings };
const o3 = { [Symbol.iterator]: 1, ...symbols };