Skip to content

Commit 41dc625

Browse files
authored
Add static index signature (#37797)
* Add static index * fix lint * make lint happy * adjust test cases * add more cases * fix changes * Add more case * accept baseline * fix error if extends others * Update vfsUtil.ts * use equal to empty array * static signature of interface is an error * Accept baseline * Check index constraints for static signature * Accpet baseline * Fix crash * fix crash * Accept baseline * Fix regression * Fix crash * always return new array
1 parent 2d6a490 commit 41dc625

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1490
-70
lines changed

src/compiler/checker.ts

+43-8
Original file line numberDiff line numberDiff line change
@@ -3891,6 +3891,12 @@ namespace ts {
38913891
return result || emptyArray;
38923892
}
38933893

3894+
function getNamedOrIndexSignatureMembers(members: SymbolTable): Symbol[] {
3895+
const result = getNamedMembers(members);
3896+
const index = getIndexSymbolFromSymbolTable(members);
3897+
return index ? concatenate(result, [index]) : result;
3898+
}
3899+
38943900
function setStructuredTypeMembers(type: StructuredType, members: SymbolTable, callSignatures: readonly Signature[], constructSignatures: readonly Signature[], stringIndexInfo: IndexInfo | undefined, numberIndexInfo: IndexInfo | undefined): ResolvedType {
38953901
const resolved = <ResolvedType>type;
38963902
resolved.members = members;
@@ -10764,6 +10770,7 @@ namespace ts {
1076410770
// Combinations of function, class, enum and module
1076510771
let members = emptySymbols;
1076610772
let stringIndexInfo: IndexInfo | undefined;
10773+
let numberIndexInfo: IndexInfo | undefined;
1076710774
if (symbol.exports) {
1076810775
members = getExportsOfSymbol(symbol);
1076910776
if (symbol === globalThisSymbol) {
@@ -10776,20 +10783,32 @@ namespace ts {
1077610783
members = varsOnly;
1077710784
}
1077810785
}
10786+
let baseConstructorIndexInfo: IndexInfo | undefined;
1077910787
setStructuredTypeMembers(type, members, emptyArray, emptyArray, undefined, undefined);
1078010788
if (symbol.flags & SymbolFlags.Class) {
1078110789
const classType = getDeclaredTypeOfClassOrInterface(symbol);
1078210790
const baseConstructorType = getBaseConstructorTypeOfClass(classType);
1078310791
if (baseConstructorType.flags & (TypeFlags.Object | TypeFlags.Intersection | TypeFlags.TypeVariable)) {
10784-
members = createSymbolTable(getNamedMembers(members));
10792+
members = createSymbolTable(getNamedOrIndexSignatureMembers(members));
1078510793
addInheritedMembers(members, getPropertiesOfType(baseConstructorType));
1078610794
}
1078710795
else if (baseConstructorType === anyType) {
10788-
stringIndexInfo = createIndexInfo(anyType, /*isReadonly*/ false);
10796+
baseConstructorIndexInfo = createIndexInfo(anyType, /*isReadonly*/ false);
10797+
}
10798+
}
10799+
10800+
const indexSymbol = getIndexSymbolFromSymbolTable(members);
10801+
if (indexSymbol) {
10802+
stringIndexInfo = getIndexInfoOfIndexSymbol(indexSymbol, IndexKind.String);
10803+
numberIndexInfo = getIndexInfoOfIndexSymbol(indexSymbol, IndexKind.Number);
10804+
}
10805+
else {
10806+
stringIndexInfo = baseConstructorIndexInfo;
10807+
if (symbol.flags & SymbolFlags.Enum && (getDeclaredTypeOfSymbol(symbol).flags & TypeFlags.Enum ||
10808+
some(type.properties, prop => !!(getTypeOfSymbol(prop).flags & TypeFlags.NumberLike)))) {
10809+
numberIndexInfo = enumNumberIndexInfo;
1078910810
}
1079010811
}
10791-
const numberIndexInfo = symbol.flags & SymbolFlags.Enum && (getDeclaredTypeOfSymbol(symbol).flags & TypeFlags.Enum ||
10792-
some(type.properties, prop => !!(getTypeOfSymbol(prop).flags & TypeFlags.NumberLike))) ? enumNumberIndexInfo : undefined;
1079310812
setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo);
1079410813
// We resolve the members before computing the signatures because a signature may use
1079510814
// typeof with a qualified name expression that circularly references the type we are
@@ -10817,6 +10836,13 @@ namespace ts {
1081710836
}
1081810837
}
1081910838

10839+
function getIndexInfoOfIndexSymbol(indexSymbol: Symbol, indexKind: IndexKind) {
10840+
const declaration = getIndexDeclarationOfIndexSymbol(indexSymbol, indexKind);
10841+
if (!declaration) return undefined;
10842+
return createIndexInfo(declaration.type ? getTypeFromTypeNode(declaration.type) : anyType,
10843+
hasEffectiveModifier(declaration, ModifierFlags.Readonly), declaration);
10844+
}
10845+
1082010846
function resolveReverseMappedTypeMembers(type: ReverseMappedType) {
1082110847
const indexInfo = getIndexInfoOfType(type.source, IndexKind.String);
1082210848
const modifiers = getMappedTypeModifiers(type.mappedType);
@@ -12363,12 +12389,20 @@ namespace ts {
1236312389
}
1236412390

1236512391
function getIndexSymbol(symbol: Symbol): Symbol | undefined {
12366-
return symbol.members!.get(InternalSymbolName.Index);
12392+
return symbol.members ? getIndexSymbolFromSymbolTable(symbol.members) : undefined;
12393+
}
12394+
12395+
function getIndexSymbolFromSymbolTable(symbolTable: SymbolTable): Symbol | undefined {
12396+
return symbolTable.get(InternalSymbolName.Index);
12397+
}
12398+
12399+
function getIndexDeclarationOfSymbol(symbol: Symbol | undefined, kind: IndexKind): IndexSignatureDeclaration | undefined {
12400+
const indexSymbol = symbol && getIndexSymbol(symbol);
12401+
return indexSymbol && getIndexDeclarationOfIndexSymbol(indexSymbol, kind);
1236712402
}
1236812403

12369-
function getIndexDeclarationOfSymbol(symbol: Symbol, kind: IndexKind): IndexSignatureDeclaration | undefined {
12404+
function getIndexDeclarationOfIndexSymbol(indexSymbol: Symbol, kind: IndexKind): IndexSignatureDeclaration | undefined {
1237012405
const syntaxKind = kind === IndexKind.Number ? SyntaxKind.NumberKeyword : SyntaxKind.StringKeyword;
12371-
const indexSymbol = getIndexSymbol(symbol);
1237212406
if (indexSymbol?.declarations) {
1237312407
for (const decl of indexSymbol.declarations) {
1237412408
const node = cast(decl, isIndexSignatureDeclaration);
@@ -36723,6 +36757,7 @@ namespace ts {
3672336757

3672436758
if (produceDiagnostics) {
3672536759
checkIndexConstraints(type);
36760+
checkIndexConstraints(staticType);
3672636761
checkTypeForDuplicateIndexSignatures(node);
3672736762
checkPropertyInitialization(node);
3672836763
}
@@ -40109,7 +40144,7 @@ namespace ts {
4010940144
if (node.kind === SyntaxKind.PropertySignature || node.kind === SyntaxKind.MethodSignature) {
4011040145
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_type_member, tokenToString(modifier.kind));
4011140146
}
40112-
if (node.kind === SyntaxKind.IndexSignature) {
40147+
if (node.kind === SyntaxKind.IndexSignature && (modifier.kind !== SyntaxKind.StaticKeyword || !isClassLike(node.parent))) {
4011340148
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_an_index_signature, tokenToString(modifier.kind));
4011440149
}
4011540150
}
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
tests/cases/conformance/parser/ecmascript5/IndexMemberDeclarations/parserIndexMemberDeclaration10.ts(2,4): error TS1071: 'static' modifier cannot appear on an index signature.
1+
tests/cases/conformance/parser/ecmascript5/IndexMemberDeclarations/parserIndexMemberDeclaration10.ts(2,11): error TS1030: 'static' modifier already seen.
22

33

44
==== tests/cases/conformance/parser/ecmascript5/IndexMemberDeclarations/parserIndexMemberDeclaration10.ts (1 errors) ====
55
class C {
66
static static [x: string]: string;
7-
~~~~~~
8-
!!! error TS1071: 'static' modifier cannot appear on an index signature.
7+
~~~~~~
8+
!!! error TS1030: 'static' modifier already seen.
99
}

tests/baselines/reference/parserIndexMemberDeclaration6.errors.txt

-9
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
tests/cases/conformance/parser/ecmascript6/Symbols/parserSymbolIndexer3.ts(2,5): error TS1071: 'static' modifier cannot appear on an index signature.
1+
tests/cases/conformance/parser/ecmascript6/Symbols/parserSymbolIndexer3.ts(2,13): error TS1023: An index signature parameter type must be either 'string' or 'number'.
22

33

44
==== tests/cases/conformance/parser/ecmascript6/Symbols/parserSymbolIndexer3.ts (1 errors) ====
55
class C {
66
static [s: symbol]: string;
7-
~~~~~~
8-
!!! error TS1071: 'static' modifier cannot appear on an index signature.
7+
~
8+
!!! error TS1023: An index signature parameter type must be either 'string' or 'number'.
99
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
tests/cases/conformance/classes/staticIndexSignature/staticIndexSignature1.ts(10,1): error TS2322: Type '2' is not assignable to type '42'.
2+
3+
4+
==== tests/cases/conformance/classes/staticIndexSignature/staticIndexSignature1.ts (1 errors) ====
5+
class C {
6+
static [s: string]: number;
7+
static [s: number]: 42
8+
}
9+
10+
C["foo"] = 1
11+
C.bar = 2;
12+
const foo = C["foo"]
13+
C[42] = 42
14+
C[2] = 2;
15+
~~~~
16+
!!! error TS2322: Type '2' is not assignable to type '42'.
17+
const bar = C[42]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//// [staticIndexSignature1.ts]
2+
class C {
3+
static [s: string]: number;
4+
static [s: number]: 42
5+
}
6+
7+
C["foo"] = 1
8+
C.bar = 2;
9+
const foo = C["foo"]
10+
C[42] = 42
11+
C[2] = 2;
12+
const bar = C[42]
13+
14+
//// [staticIndexSignature1.js]
15+
var C = /** @class */ (function () {
16+
function C() {
17+
}
18+
return C;
19+
}());
20+
C["foo"] = 1;
21+
C.bar = 2;
22+
var foo = C["foo"];
23+
C[42] = 42;
24+
C[2] = 2;
25+
var bar = C[42];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
=== tests/cases/conformance/classes/staticIndexSignature/staticIndexSignature1.ts ===
2+
class C {
3+
>C : Symbol(C, Decl(staticIndexSignature1.ts, 0, 0))
4+
5+
static [s: string]: number;
6+
>s : Symbol(s, Decl(staticIndexSignature1.ts, 1, 12))
7+
8+
static [s: number]: 42
9+
>s : Symbol(s, Decl(staticIndexSignature1.ts, 2, 12))
10+
}
11+
12+
C["foo"] = 1
13+
>C : Symbol(C, Decl(staticIndexSignature1.ts, 0, 0))
14+
15+
C.bar = 2;
16+
>C : Symbol(C, Decl(staticIndexSignature1.ts, 0, 0))
17+
18+
const foo = C["foo"]
19+
>foo : Symbol(foo, Decl(staticIndexSignature1.ts, 7, 5))
20+
>C : Symbol(C, Decl(staticIndexSignature1.ts, 0, 0))
21+
22+
C[42] = 42
23+
>C : Symbol(C, Decl(staticIndexSignature1.ts, 0, 0))
24+
25+
C[2] = 2;
26+
>C : Symbol(C, Decl(staticIndexSignature1.ts, 0, 0))
27+
28+
const bar = C[42]
29+
>bar : Symbol(bar, Decl(staticIndexSignature1.ts, 10, 5))
30+
>C : Symbol(C, Decl(staticIndexSignature1.ts, 0, 0))
31+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
=== tests/cases/conformance/classes/staticIndexSignature/staticIndexSignature1.ts ===
2+
class C {
3+
>C : C
4+
5+
static [s: string]: number;
6+
>s : string
7+
8+
static [s: number]: 42
9+
>s : number
10+
}
11+
12+
C["foo"] = 1
13+
>C["foo"] = 1 : 1
14+
>C["foo"] : number
15+
>C : typeof C
16+
>"foo" : "foo"
17+
>1 : 1
18+
19+
C.bar = 2;
20+
>C.bar = 2 : 2
21+
>C.bar : number
22+
>C : typeof C
23+
>bar : number
24+
>2 : 2
25+
26+
const foo = C["foo"]
27+
>foo : number
28+
>C["foo"] : number
29+
>C : typeof C
30+
>"foo" : "foo"
31+
32+
C[42] = 42
33+
>C[42] = 42 : 42
34+
>C[42] : 42
35+
>C : typeof C
36+
>42 : 42
37+
>42 : 42
38+
39+
C[2] = 2;
40+
>C[2] = 2 : 2
41+
>C[2] : 42
42+
>C : typeof C
43+
>2 : 2
44+
>2 : 2
45+
46+
const bar = C[42]
47+
>bar : 42
48+
>C[42] : 42
49+
>C : typeof C
50+
>42 : 42
51+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
tests/cases/conformance/classes/staticIndexSignature/staticIndexSignature2.ts(6,1): error TS2542: Index signature in type 'typeof C' only permits reading.
2+
tests/cases/conformance/classes/staticIndexSignature/staticIndexSignature2.ts(7,1): error TS2542: Index signature in type 'typeof C' only permits reading.
3+
tests/cases/conformance/classes/staticIndexSignature/staticIndexSignature2.ts(9,1): error TS2542: Index signature in type 'typeof C' only permits reading.
4+
tests/cases/conformance/classes/staticIndexSignature/staticIndexSignature2.ts(10,1): error TS2322: Type '2' is not assignable to type '42'.
5+
tests/cases/conformance/classes/staticIndexSignature/staticIndexSignature2.ts(10,1): error TS2542: Index signature in type 'typeof C' only permits reading.
6+
7+
8+
==== tests/cases/conformance/classes/staticIndexSignature/staticIndexSignature2.ts (5 errors) ====
9+
class C {
10+
static readonly [s: string]: number;
11+
static readonly [s: number]: 42
12+
}
13+
14+
C["foo"] = 1
15+
~~~~~~~~
16+
!!! error TS2542: Index signature in type 'typeof C' only permits reading.
17+
C.bar = 2;
18+
~~~~~
19+
!!! error TS2542: Index signature in type 'typeof C' only permits reading.
20+
const foo = C["foo"]
21+
C[42] = 42
22+
~~~~~
23+
!!! error TS2542: Index signature in type 'typeof C' only permits reading.
24+
C[2] = 2;
25+
~~~~
26+
!!! error TS2322: Type '2' is not assignable to type '42'.
27+
~~~~
28+
!!! error TS2542: Index signature in type 'typeof C' only permits reading.
29+
const bar = C[42]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//// [staticIndexSignature2.ts]
2+
class C {
3+
static readonly [s: string]: number;
4+
static readonly [s: number]: 42
5+
}
6+
7+
C["foo"] = 1
8+
C.bar = 2;
9+
const foo = C["foo"]
10+
C[42] = 42
11+
C[2] = 2;
12+
const bar = C[42]
13+
14+
//// [staticIndexSignature2.js]
15+
var C = /** @class */ (function () {
16+
function C() {
17+
}
18+
return C;
19+
}());
20+
C["foo"] = 1;
21+
C.bar = 2;
22+
var foo = C["foo"];
23+
C[42] = 42;
24+
C[2] = 2;
25+
var bar = C[42];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
=== tests/cases/conformance/classes/staticIndexSignature/staticIndexSignature2.ts ===
2+
class C {
3+
>C : Symbol(C, Decl(staticIndexSignature2.ts, 0, 0))
4+
5+
static readonly [s: string]: number;
6+
>s : Symbol(s, Decl(staticIndexSignature2.ts, 1, 21))
7+
8+
static readonly [s: number]: 42
9+
>s : Symbol(s, Decl(staticIndexSignature2.ts, 2, 21))
10+
}
11+
12+
C["foo"] = 1
13+
>C : Symbol(C, Decl(staticIndexSignature2.ts, 0, 0))
14+
15+
C.bar = 2;
16+
>C : Symbol(C, Decl(staticIndexSignature2.ts, 0, 0))
17+
18+
const foo = C["foo"]
19+
>foo : Symbol(foo, Decl(staticIndexSignature2.ts, 7, 5))
20+
>C : Symbol(C, Decl(staticIndexSignature2.ts, 0, 0))
21+
22+
C[42] = 42
23+
>C : Symbol(C, Decl(staticIndexSignature2.ts, 0, 0))
24+
25+
C[2] = 2;
26+
>C : Symbol(C, Decl(staticIndexSignature2.ts, 0, 0))
27+
28+
const bar = C[42]
29+
>bar : Symbol(bar, Decl(staticIndexSignature2.ts, 10, 5))
30+
>C : Symbol(C, Decl(staticIndexSignature2.ts, 0, 0))
31+

0 commit comments

Comments
 (0)