Skip to content

Commit fa0080f

Browse files
authored
Support interpreting non-literal computed properties in classes as implicit index signatures (#59860)
1 parent e24cc01 commit fa0080f

34 files changed

+400
-81
lines changed

src/compiler/checker.ts

Lines changed: 148 additions & 30 deletions
Large diffs are not rendered by default.

src/compiler/emitter.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1167,6 +1167,7 @@ export const notImplementedResolver: EmitResolver = {
11671167
getDeclarationStatementsForSourceFile: notImplemented,
11681168
isImportRequiredByAugmentation: notImplemented,
11691169
isDefinitelyReferenceToGlobalSymbolObject: notImplemented,
1170+
createLateBoundIndexSignatures: notImplemented,
11701171
};
11711172

11721173
const enum PipelinePhase {

src/compiler/transformers/declarations.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
canHaveModifiers,
1313
canProduceDiagnostics,
1414
ClassDeclaration,
15+
ClassElement,
1516
compact,
1617
concatenate,
1718
ConditionalTypeNode,
@@ -1652,7 +1653,8 @@ export function transformDeclarations(context: TransformationContext): Transform
16521653
/*initializer*/ undefined,
16531654
),
16541655
] : undefined;
1655-
const memberNodes = concatenate(concatenate(privateIdentifier, parameterProperties), visitNodes(input.members, visitDeclarationSubtree, isClassElement));
1656+
const lateIndexes = resolver.createLateBoundIndexSignatures(input, enclosingDeclaration, declarationEmitNodeBuilderFlags, declarationEmitInternalNodeBuilderFlags, symbolTracker);
1657+
const memberNodes = concatenate(concatenate(concatenate<ClassElement>(privateIdentifier, lateIndexes), parameterProperties), visitNodes(input.members, visitDeclarationSubtree, isClassElement));
16561658
const members = factory.createNodeArray(memberNodes);
16571659

16581660
const extendsClause = getEffectiveBaseTypeNode(input);

src/compiler/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5048,7 +5048,7 @@ export interface TypeChecker {
50485048
/** @internal */ getTypeOfPropertyOfType(type: Type, propertyName: string): Type | undefined;
50495049
getIndexInfoOfType(type: Type, kind: IndexKind): IndexInfo | undefined;
50505050
getIndexInfosOfType(type: Type): readonly IndexInfo[];
5051-
getIndexInfosOfIndexSymbol: (indexSymbol: Symbol) => IndexInfo[];
5051+
getIndexInfosOfIndexSymbol: (indexSymbol: Symbol, siblingSymbols?: Symbol[] | undefined) => IndexInfo[];
50525052
getSignaturesOfType(type: Type, kind: SignatureKind): readonly Signature[];
50535053
getIndexTypeOfType(type: Type, kind: IndexKind): Type | undefined;
50545054
/** @internal */ getIndexType(type: Type): Type;
@@ -5867,6 +5867,7 @@ export interface EmitResolver {
58675867
getDeclarationStatementsForSourceFile(node: SourceFile, flags: NodeBuilderFlags, internalFlags: InternalNodeBuilderFlags, tracker: SymbolTracker): Statement[] | undefined;
58685868
isImportRequiredByAugmentation(decl: ImportDeclaration): boolean;
58695869
isDefinitelyReferenceToGlobalSymbolObject(node: Node): boolean;
5870+
createLateBoundIndexSignatures(cls: ClassLikeDeclaration, enclosingDeclaration: Node, flags: NodeBuilderFlags, internalFlags: InternalNodeBuilderFlags, tracker: SymbolTracker): IndexSignatureDeclaration[] | undefined;
58705871
}
58715872

58725873
// dprint-ignore

tests/baselines/reference/ES5SymbolProperty2.types

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ module M {
2424
> : ^^^
2525
}
2626
(new C)[Symbol.iterator];
27-
>(new C)[Symbol.iterator] : any
28-
> : ^^^
27+
>(new C)[Symbol.iterator] : () => void
28+
> : ^^^^^^^^^^
2929
>(new C) : C
3030
> : ^
3131
>new C : C
@@ -41,8 +41,8 @@ module M {
4141
}
4242

4343
(new M.C)[Symbol.iterator];
44-
>(new M.C)[Symbol.iterator] : any
45-
> : ^^^
44+
>(new M.C)[Symbol.iterator] : () => void
45+
> : ^^^^^^^^^^
4646
>(new M.C) : M.C
4747
> : ^^^
4848
>new M.C : M.C

tests/baselines/reference/ES5SymbolProperty3.types

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ class C {
1919
}
2020

2121
(new C)[Symbol.iterator]
22-
>(new C)[Symbol.iterator] : error
22+
>(new C)[Symbol.iterator] : () => void
23+
> : ^^^^^^^^^^
2324
>(new C) : C
2425
> : ^
2526
>new C : C

tests/baselines/reference/ES5SymbolProperty4.types

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ class C {
2323
}
2424

2525
(new C)[Symbol.iterator]
26-
>(new C)[Symbol.iterator] : error
26+
>(new C)[Symbol.iterator] : () => void
27+
> : ^^^^^^^^^^
2728
>(new C) : C
2829
> : ^
2930
>new C : C
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
ES5SymbolProperty5.ts(7,26): error TS2554: Expected 0 arguments, but got 1.
2+
3+
4+
==== ES5SymbolProperty5.ts (1 errors) ====
5+
var Symbol: { iterator: symbol };
6+
7+
class C {
8+
[Symbol.iterator]() { }
9+
}
10+
11+
(new C)[Symbol.iterator](0) // Should error
12+
~
13+
!!! error TS2554: Expected 0 arguments, but got 1.

tests/baselines/reference/ES5SymbolProperty5.types

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@ class C {
2323
}
2424

2525
(new C)[Symbol.iterator](0) // Should error
26-
>(new C)[Symbol.iterator](0) : error
27-
>(new C)[Symbol.iterator] : error
26+
>(new C)[Symbol.iterator](0) : void
27+
> : ^^^^
28+
>(new C)[Symbol.iterator] : () => void
29+
> : ^^^^^^^^^^
2830
>(new C) : C
2931
> : ^
3032
>new C : C

tests/baselines/reference/ES5SymbolProperty6.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ class C {
1717
}
1818

1919
(new C)[Symbol.iterator]
20-
>(new C)[Symbol.iterator] : any
21-
> : ^^^
20+
>(new C)[Symbol.iterator] : () => void
21+
> : ^^^^^^^^^^
2222
>(new C) : C
2323
> : ^
2424
>new C : C

tests/baselines/reference/ES5SymbolProperty7.types

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ class C {
2121
}
2222

2323
(new C)[Symbol.iterator]
24-
>(new C)[Symbol.iterator] : error
24+
>(new C)[Symbol.iterator] : () => void
25+
> : ^^^^^^^^^^
2526
>(new C) : C
2627
> : ^
2728
>new C : C

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6140,7 +6140,7 @@ declare namespace ts {
61406140
getPrivateIdentifierPropertyOfType(leftType: Type, name: string, location: Node): Symbol | undefined;
61416141
getIndexInfoOfType(type: Type, kind: IndexKind): IndexInfo | undefined;
61426142
getIndexInfosOfType(type: Type): readonly IndexInfo[];
6143-
getIndexInfosOfIndexSymbol: (indexSymbol: Symbol) => IndexInfo[];
6143+
getIndexInfosOfIndexSymbol: (indexSymbol: Symbol, siblingSymbols?: Symbol[] | undefined) => IndexInfo[];
61446144
getSignaturesOfType(type: Type, kind: SignatureKind): readonly Signature[];
61456145
getIndexTypeOfType(type: Type, kind: IndexKind): Type | undefined;
61466146
getBaseTypes(type: InterfaceType): BaseType[];
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//// [tests/cases/compiler/classNonUniqueSymbolMethodHasSymbolIndexer.ts] ////
2+
3+
//// [classNonUniqueSymbolMethodHasSymbolIndexer.ts]
4+
declare const a: symbol;
5+
export class A {
6+
[a]() { return 1 };
7+
}
8+
declare const e1: A[typeof a]; // no error, `A` has `symbol` index
9+
10+
type Constructor = new (...args: any[]) => {};
11+
declare function Mix<T extends Constructor>(classish: T): T & (new (...args: any[]) => {mixed: true});
12+
13+
export const Mixer = Mix(class {
14+
[a]() { return 1 };
15+
});
16+
17+
18+
//// [classNonUniqueSymbolMethodHasSymbolIndexer.js]
19+
export class A {
20+
[a]() { return 1; }
21+
;
22+
}
23+
export const Mixer = Mix(class {
24+
[a]() { return 1; }
25+
;
26+
});
27+
28+
29+
//// [classNonUniqueSymbolMethodHasSymbolIndexer.d.ts]
30+
export declare class A {
31+
[x: symbol]: () => number;
32+
}
33+
export declare const Mixer: {
34+
new (): {
35+
[x: symbol]: () => number;
36+
};
37+
} & (new (...args: any[]) => {
38+
mixed: true;
39+
});
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//// [tests/cases/compiler/classNonUniqueSymbolMethodHasSymbolIndexer.ts] ////
2+
3+
=== classNonUniqueSymbolMethodHasSymbolIndexer.ts ===
4+
declare const a: symbol;
5+
>a : Symbol(a, Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 0, 13))
6+
7+
export class A {
8+
>A : Symbol(A, Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 0, 24))
9+
10+
[a]() { return 1 };
11+
>[a] : Symbol(A[a], Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 1, 16))
12+
>a : Symbol(a, Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 0, 13))
13+
}
14+
declare const e1: A[typeof a]; // no error, `A` has `symbol` index
15+
>e1 : Symbol(e1, Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 4, 13))
16+
>A : Symbol(A, Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 0, 24))
17+
>a : Symbol(a, Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 0, 13))
18+
19+
type Constructor = new (...args: any[]) => {};
20+
>Constructor : Symbol(Constructor, Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 4, 30))
21+
>args : Symbol(args, Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 6, 24))
22+
23+
declare function Mix<T extends Constructor>(classish: T): T & (new (...args: any[]) => {mixed: true});
24+
>Mix : Symbol(Mix, Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 6, 46))
25+
>T : Symbol(T, Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 7, 21))
26+
>Constructor : Symbol(Constructor, Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 4, 30))
27+
>classish : Symbol(classish, Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 7, 44))
28+
>T : Symbol(T, Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 7, 21))
29+
>T : Symbol(T, Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 7, 21))
30+
>args : Symbol(args, Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 7, 68))
31+
>mixed : Symbol(mixed, Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 7, 88))
32+
33+
export const Mixer = Mix(class {
34+
>Mixer : Symbol(Mixer, Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 9, 12))
35+
>Mix : Symbol(Mix, Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 6, 46))
36+
37+
[a]() { return 1 };
38+
>[a] : Symbol((Anonymous class)[a], Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 9, 32))
39+
>a : Symbol(a, Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 0, 13))
40+
41+
});
42+
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
//// [tests/cases/compiler/classNonUniqueSymbolMethodHasSymbolIndexer.ts] ////
2+
3+
=== classNonUniqueSymbolMethodHasSymbolIndexer.ts ===
4+
declare const a: symbol;
5+
>a : symbol
6+
> : ^^^^^^
7+
8+
export class A {
9+
>A : A
10+
> : ^
11+
12+
[a]() { return 1 };
13+
>[a] : () => number
14+
> : ^^^^^^^^^^^^
15+
>a : symbol
16+
> : ^^^^^^
17+
>1 : 1
18+
> : ^
19+
}
20+
declare const e1: A[typeof a]; // no error, `A` has `symbol` index
21+
>e1 : () => number
22+
> : ^^^^^^^^^^^^
23+
>a : symbol
24+
> : ^^^^^^
25+
26+
type Constructor = new (...args: any[]) => {};
27+
>Constructor : Constructor
28+
> : ^^^^^^^^^^^
29+
>args : any[]
30+
> : ^^^^^
31+
32+
declare function Mix<T extends Constructor>(classish: T): T & (new (...args: any[]) => {mixed: true});
33+
>Mix : <T extends Constructor>(classish: T) => T & (new (...args: any[]) => { mixed: true; })
34+
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^
35+
>classish : T
36+
> : ^
37+
>args : any[]
38+
> : ^^^^^
39+
>mixed : true
40+
> : ^^^^
41+
>true : true
42+
> : ^^^^
43+
44+
export const Mixer = Mix(class {
45+
>Mixer : typeof (Anonymous class) & (new (...args: any[]) => { mixed: true; })
46+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^^^^ ^
47+
>Mix(class { [a]() { return 1 };}) : typeof (Anonymous class) & (new (...args: any[]) => { mixed: true; })
48+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^^^^ ^
49+
>Mix : <T extends Constructor>(classish: T) => T & (new (...args: any[]) => { mixed: true; })
50+
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^
51+
>class { [a]() { return 1 };} : typeof (Anonymous class)
52+
> : ^^^^^^^^^^^^^^^^^^^^^^^^
53+
54+
[a]() { return 1 };
55+
>[a] : () => number
56+
> : ^^^^^^^^^^^^
57+
>a : symbol
58+
> : ^^^^^^
59+
>1 : 1
60+
> : ^
61+
62+
});
63+

tests/baselines/reference/complicatedPrivacy.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,8 @@ module m1 {
8686
export function f4(arg1:
8787
>f4 : (arg1: { [number]: C1; }) => void
8888
> : ^ ^^ ^^^^^^^^^
89-
>arg1 : {}
90-
> : ^^
89+
>arg1 : { [x: number]: C1; }
90+
> : ^^^^^^^^^^^^^^^^^^^^
9191
{
9292
[number]: C1; // Used to be indexer, now it is a computed property
9393
>[number] : C1

tests/baselines/reference/computedPropertyNames12_ES5.errors.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ computedPropertyNames12_ES5.ts(6,5): error TS1166: A computed property name in a
33
computedPropertyNames12_ES5.ts(7,12): error TS1166: A computed property name in a class property declaration must have a simple literal type or a 'unique symbol' type.
44
computedPropertyNames12_ES5.ts(8,5): error TS1166: A computed property name in a class property declaration must have a simple literal type or a 'unique symbol' type.
55
computedPropertyNames12_ES5.ts(9,5): error TS1166: A computed property name in a class property declaration must have a simple literal type or a 'unique symbol' type.
6+
computedPropertyNames12_ES5.ts(9,5): error TS2411: Property '[+s]' of type 'string' is not assignable to 'number' index type 'number'.
7+
computedPropertyNames12_ES5.ts(9,5): error TS2411: Property '[+s]' of type 'string' is not assignable to 'string' index type 'number'.
68
computedPropertyNames12_ES5.ts(12,5): error TS1166: A computed property name in a class property declaration must have a simple literal type or a 'unique symbol' type.
79
computedPropertyNames12_ES5.ts(13,12): error TS1166: A computed property name in a class property declaration must have a simple literal type or a 'unique symbol' type.
810
computedPropertyNames12_ES5.ts(15,12): error TS1166: A computed property name in a class property declaration must have a simple literal type or a 'unique symbol' type.
911

1012

11-
==== computedPropertyNames12_ES5.ts (8 errors) ====
13+
==== computedPropertyNames12_ES5.ts (10 errors) ====
1214
var s: string;
1315
var n: number;
1416
var a: any;
@@ -28,6 +30,10 @@ computedPropertyNames12_ES5.ts(15,12): error TS1166: A computed property name in
2830
[+s]: typeof s;
2931
~~~~
3032
!!! error TS1166: A computed property name in a class property declaration must have a simple literal type or a 'unique symbol' type.
33+
~~~~
34+
!!! error TS2411: Property '[+s]' of type 'string' is not assignable to 'number' index type 'number'.
35+
~~~~
36+
!!! error TS2411: Property '[+s]' of type 'string' is not assignable to 'string' index type 'number'.
3137
static [""]: number;
3238
[0]: number;
3339
[a]: number;

tests/baselines/reference/computedPropertyNames12_ES6.errors.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ computedPropertyNames12_ES6.ts(6,5): error TS1166: A computed property name in a
33
computedPropertyNames12_ES6.ts(7,12): error TS1166: A computed property name in a class property declaration must have a simple literal type or a 'unique symbol' type.
44
computedPropertyNames12_ES6.ts(8,5): error TS1166: A computed property name in a class property declaration must have a simple literal type or a 'unique symbol' type.
55
computedPropertyNames12_ES6.ts(9,5): error TS1166: A computed property name in a class property declaration must have a simple literal type or a 'unique symbol' type.
6+
computedPropertyNames12_ES6.ts(9,5): error TS2411: Property '[+s]' of type 'string' is not assignable to 'number' index type 'number'.
7+
computedPropertyNames12_ES6.ts(9,5): error TS2411: Property '[+s]' of type 'string' is not assignable to 'string' index type 'number'.
68
computedPropertyNames12_ES6.ts(12,5): error TS1166: A computed property name in a class property declaration must have a simple literal type or a 'unique symbol' type.
79
computedPropertyNames12_ES6.ts(13,12): error TS1166: A computed property name in a class property declaration must have a simple literal type or a 'unique symbol' type.
810
computedPropertyNames12_ES6.ts(15,12): error TS1166: A computed property name in a class property declaration must have a simple literal type or a 'unique symbol' type.
911

1012

11-
==== computedPropertyNames12_ES6.ts (8 errors) ====
13+
==== computedPropertyNames12_ES6.ts (10 errors) ====
1214
var s: string;
1315
var n: number;
1416
var a: any;
@@ -28,6 +30,10 @@ computedPropertyNames12_ES6.ts(15,12): error TS1166: A computed property name in
2830
[+s]: typeof s;
2931
~~~~
3032
!!! error TS1166: A computed property name in a class property declaration must have a simple literal type or a 'unique symbol' type.
33+
~~~~
34+
!!! error TS2411: Property '[+s]' of type 'string' is not assignable to 'number' index type 'number'.
35+
~~~~
36+
!!! error TS2411: Property '[+s]' of type 'string' is not assignable to 'string' index type 'number'.
3137
static [""]: number;
3238
[0]: number;
3339
[a]: number;

tests/baselines/reference/declarationEmitAnyComputedPropertyInClass.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,5 @@ exports.C = C;
2828

2929
//// [main.d.ts]
3030
export declare class C {
31+
[x: number]: () => void;
3132
}

tests/baselines/reference/declarationEmitComputedPropertyNameEnum2.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
export type Type = { x?: { [Enum.A]: 0 } };
55
>Type : Type
66
> : ^^^^
7-
>x : {} | undefined
8-
> : ^^^^^^^^^^^^^^
7+
>x : { [x: number]: 0; } | undefined
8+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
99
>[Enum.A] : 0
1010
> : ^
1111
>Enum.A : any

tests/baselines/reference/parserComputedPropertyName13.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
=== parserComputedPropertyName13.ts ===
44
var v: { [e]: number };
5-
>v : {}
6-
> : ^^
5+
>v : { [x: number]: number; }
6+
> : ^^^^^^^^^^^^^^^^^^^^^^^^
77
>[e] : number
88
> : ^^^^^^
99
>e : any

tests/baselines/reference/parserComputedPropertyName14.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
=== parserComputedPropertyName14.ts ===
44
var v: { [e](): number };
5-
>v : {}
6-
> : ^^
5+
>v : { [x: number]: () => number; }
6+
> : ^^^^^^^^^^^^^^^^^^^^^ ^^^
77
>[e] : () => number
88
> : ^^^^^^
99
>e : any

0 commit comments

Comments
 (0)