Skip to content

Commit

Permalink
Add static index signature (#37797)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Kingwl authored Mar 26, 2021
1 parent 2d6a490 commit 41dc625
Show file tree
Hide file tree
Showing 43 changed files with 1,490 additions and 70 deletions.
51 changes: 43 additions & 8 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3891,6 +3891,12 @@ namespace ts {
return result || emptyArray;
}

function getNamedOrIndexSignatureMembers(members: SymbolTable): Symbol[] {
const result = getNamedMembers(members);
const index = getIndexSymbolFromSymbolTable(members);
return index ? concatenate(result, [index]) : result;
}

function setStructuredTypeMembers(type: StructuredType, members: SymbolTable, callSignatures: readonly Signature[], constructSignatures: readonly Signature[], stringIndexInfo: IndexInfo | undefined, numberIndexInfo: IndexInfo | undefined): ResolvedType {
const resolved = <ResolvedType>type;
resolved.members = members;
Expand Down Expand Up @@ -10764,6 +10770,7 @@ namespace ts {
// Combinations of function, class, enum and module
let members = emptySymbols;
let stringIndexInfo: IndexInfo | undefined;
let numberIndexInfo: IndexInfo | undefined;
if (symbol.exports) {
members = getExportsOfSymbol(symbol);
if (symbol === globalThisSymbol) {
Expand All @@ -10776,20 +10783,32 @@ namespace ts {
members = varsOnly;
}
}
let baseConstructorIndexInfo: IndexInfo | undefined;
setStructuredTypeMembers(type, members, emptyArray, emptyArray, undefined, undefined);
if (symbol.flags & SymbolFlags.Class) {
const classType = getDeclaredTypeOfClassOrInterface(symbol);
const baseConstructorType = getBaseConstructorTypeOfClass(classType);
if (baseConstructorType.flags & (TypeFlags.Object | TypeFlags.Intersection | TypeFlags.TypeVariable)) {
members = createSymbolTable(getNamedMembers(members));
members = createSymbolTable(getNamedOrIndexSignatureMembers(members));
addInheritedMembers(members, getPropertiesOfType(baseConstructorType));
}
else if (baseConstructorType === anyType) {
stringIndexInfo = createIndexInfo(anyType, /*isReadonly*/ false);
baseConstructorIndexInfo = createIndexInfo(anyType, /*isReadonly*/ false);
}
}

const indexSymbol = getIndexSymbolFromSymbolTable(members);
if (indexSymbol) {
stringIndexInfo = getIndexInfoOfIndexSymbol(indexSymbol, IndexKind.String);
numberIndexInfo = getIndexInfoOfIndexSymbol(indexSymbol, IndexKind.Number);
}
else {
stringIndexInfo = baseConstructorIndexInfo;
if (symbol.flags & SymbolFlags.Enum && (getDeclaredTypeOfSymbol(symbol).flags & TypeFlags.Enum ||
some(type.properties, prop => !!(getTypeOfSymbol(prop).flags & TypeFlags.NumberLike)))) {
numberIndexInfo = enumNumberIndexInfo;
}
}
const numberIndexInfo = symbol.flags & SymbolFlags.Enum && (getDeclaredTypeOfSymbol(symbol).flags & TypeFlags.Enum ||
some(type.properties, prop => !!(getTypeOfSymbol(prop).flags & TypeFlags.NumberLike))) ? enumNumberIndexInfo : undefined;
setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo);
// We resolve the members before computing the signatures because a signature may use
// typeof with a qualified name expression that circularly references the type we are
Expand Down Expand Up @@ -10817,6 +10836,13 @@ namespace ts {
}
}

function getIndexInfoOfIndexSymbol(indexSymbol: Symbol, indexKind: IndexKind) {
const declaration = getIndexDeclarationOfIndexSymbol(indexSymbol, indexKind);
if (!declaration) return undefined;
return createIndexInfo(declaration.type ? getTypeFromTypeNode(declaration.type) : anyType,
hasEffectiveModifier(declaration, ModifierFlags.Readonly), declaration);
}

function resolveReverseMappedTypeMembers(type: ReverseMappedType) {
const indexInfo = getIndexInfoOfType(type.source, IndexKind.String);
const modifiers = getMappedTypeModifiers(type.mappedType);
Expand Down Expand Up @@ -12363,12 +12389,20 @@ namespace ts {
}

function getIndexSymbol(symbol: Symbol): Symbol | undefined {
return symbol.members!.get(InternalSymbolName.Index);
return symbol.members ? getIndexSymbolFromSymbolTable(symbol.members) : undefined;
}

function getIndexSymbolFromSymbolTable(symbolTable: SymbolTable): Symbol | undefined {
return symbolTable.get(InternalSymbolName.Index);
}

function getIndexDeclarationOfSymbol(symbol: Symbol | undefined, kind: IndexKind): IndexSignatureDeclaration | undefined {
const indexSymbol = symbol && getIndexSymbol(symbol);
return indexSymbol && getIndexDeclarationOfIndexSymbol(indexSymbol, kind);
}

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

if (produceDiagnostics) {
checkIndexConstraints(type);
checkIndexConstraints(staticType);
checkTypeForDuplicateIndexSignatures(node);
checkPropertyInitialization(node);
}
Expand Down Expand Up @@ -40109,7 +40144,7 @@ namespace ts {
if (node.kind === SyntaxKind.PropertySignature || node.kind === SyntaxKind.MethodSignature) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_type_member, tokenToString(modifier.kind));
}
if (node.kind === SyntaxKind.IndexSignature) {
if (node.kind === SyntaxKind.IndexSignature && (modifier.kind !== SyntaxKind.StaticKeyword || !isClassLike(node.parent))) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_an_index_signature, tokenToString(modifier.kind));
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
tests/cases/conformance/parser/ecmascript5/IndexMemberDeclarations/parserIndexMemberDeclaration10.ts(2,4): error TS1071: 'static' modifier cannot appear on an index signature.
tests/cases/conformance/parser/ecmascript5/IndexMemberDeclarations/parserIndexMemberDeclaration10.ts(2,11): error TS1030: 'static' modifier already seen.


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

This file was deleted.

6 changes: 3 additions & 3 deletions tests/baselines/reference/parserSymbolIndexer3.errors.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
tests/cases/conformance/parser/ecmascript6/Symbols/parserSymbolIndexer3.ts(2,5): error TS1071: 'static' modifier cannot appear on an index signature.
tests/cases/conformance/parser/ecmascript6/Symbols/parserSymbolIndexer3.ts(2,13): error TS1023: An index signature parameter type must be either 'string' or 'number'.


==== tests/cases/conformance/parser/ecmascript6/Symbols/parserSymbolIndexer3.ts (1 errors) ====
class C {
static [s: symbol]: string;
~~~~~~
!!! error TS1071: 'static' modifier cannot appear on an index signature.
~
!!! error TS1023: An index signature parameter type must be either 'string' or 'number'.
}
17 changes: 17 additions & 0 deletions tests/baselines/reference/staticIndexSignature1.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
tests/cases/conformance/classes/staticIndexSignature/staticIndexSignature1.ts(10,1): error TS2322: Type '2' is not assignable to type '42'.


==== tests/cases/conformance/classes/staticIndexSignature/staticIndexSignature1.ts (1 errors) ====
class C {
static [s: string]: number;
static [s: number]: 42
}

C["foo"] = 1
C.bar = 2;
const foo = C["foo"]
C[42] = 42
C[2] = 2;
~~~~
!!! error TS2322: Type '2' is not assignable to type '42'.
const bar = C[42]
25 changes: 25 additions & 0 deletions tests/baselines/reference/staticIndexSignature1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//// [staticIndexSignature1.ts]
class C {
static [s: string]: number;
static [s: number]: 42
}

C["foo"] = 1
C.bar = 2;
const foo = C["foo"]
C[42] = 42
C[2] = 2;
const bar = C[42]

//// [staticIndexSignature1.js]
var C = /** @class */ (function () {
function C() {
}
return C;
}());
C["foo"] = 1;
C.bar = 2;
var foo = C["foo"];
C[42] = 42;
C[2] = 2;
var bar = C[42];
31 changes: 31 additions & 0 deletions tests/baselines/reference/staticIndexSignature1.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
=== tests/cases/conformance/classes/staticIndexSignature/staticIndexSignature1.ts ===
class C {
>C : Symbol(C, Decl(staticIndexSignature1.ts, 0, 0))

static [s: string]: number;
>s : Symbol(s, Decl(staticIndexSignature1.ts, 1, 12))

static [s: number]: 42
>s : Symbol(s, Decl(staticIndexSignature1.ts, 2, 12))
}

C["foo"] = 1
>C : Symbol(C, Decl(staticIndexSignature1.ts, 0, 0))

C.bar = 2;
>C : Symbol(C, Decl(staticIndexSignature1.ts, 0, 0))

const foo = C["foo"]
>foo : Symbol(foo, Decl(staticIndexSignature1.ts, 7, 5))
>C : Symbol(C, Decl(staticIndexSignature1.ts, 0, 0))

C[42] = 42
>C : Symbol(C, Decl(staticIndexSignature1.ts, 0, 0))

C[2] = 2;
>C : Symbol(C, Decl(staticIndexSignature1.ts, 0, 0))

const bar = C[42]
>bar : Symbol(bar, Decl(staticIndexSignature1.ts, 10, 5))
>C : Symbol(C, Decl(staticIndexSignature1.ts, 0, 0))

51 changes: 51 additions & 0 deletions tests/baselines/reference/staticIndexSignature1.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
=== tests/cases/conformance/classes/staticIndexSignature/staticIndexSignature1.ts ===
class C {
>C : C

static [s: string]: number;
>s : string

static [s: number]: 42
>s : number
}

C["foo"] = 1
>C["foo"] = 1 : 1
>C["foo"] : number
>C : typeof C
>"foo" : "foo"
>1 : 1

C.bar = 2;
>C.bar = 2 : 2
>C.bar : number
>C : typeof C
>bar : number
>2 : 2

const foo = C["foo"]
>foo : number
>C["foo"] : number
>C : typeof C
>"foo" : "foo"

C[42] = 42
>C[42] = 42 : 42
>C[42] : 42
>C : typeof C
>42 : 42
>42 : 42

C[2] = 2;
>C[2] = 2 : 2
>C[2] : 42
>C : typeof C
>2 : 2
>2 : 2

const bar = C[42]
>bar : 42
>C[42] : 42
>C : typeof C
>42 : 42

29 changes: 29 additions & 0 deletions tests/baselines/reference/staticIndexSignature2.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
tests/cases/conformance/classes/staticIndexSignature/staticIndexSignature2.ts(6,1): error TS2542: Index signature in type 'typeof C' only permits reading.
tests/cases/conformance/classes/staticIndexSignature/staticIndexSignature2.ts(7,1): error TS2542: Index signature in type 'typeof C' only permits reading.
tests/cases/conformance/classes/staticIndexSignature/staticIndexSignature2.ts(9,1): error TS2542: Index signature in type 'typeof C' only permits reading.
tests/cases/conformance/classes/staticIndexSignature/staticIndexSignature2.ts(10,1): error TS2322: Type '2' is not assignable to type '42'.
tests/cases/conformance/classes/staticIndexSignature/staticIndexSignature2.ts(10,1): error TS2542: Index signature in type 'typeof C' only permits reading.


==== tests/cases/conformance/classes/staticIndexSignature/staticIndexSignature2.ts (5 errors) ====
class C {
static readonly [s: string]: number;
static readonly [s: number]: 42
}

C["foo"] = 1
~~~~~~~~
!!! error TS2542: Index signature in type 'typeof C' only permits reading.
C.bar = 2;
~~~~~
!!! error TS2542: Index signature in type 'typeof C' only permits reading.
const foo = C["foo"]
C[42] = 42
~~~~~
!!! error TS2542: Index signature in type 'typeof C' only permits reading.
C[2] = 2;
~~~~
!!! error TS2322: Type '2' is not assignable to type '42'.
~~~~
!!! error TS2542: Index signature in type 'typeof C' only permits reading.
const bar = C[42]
25 changes: 25 additions & 0 deletions tests/baselines/reference/staticIndexSignature2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//// [staticIndexSignature2.ts]
class C {
static readonly [s: string]: number;
static readonly [s: number]: 42
}

C["foo"] = 1
C.bar = 2;
const foo = C["foo"]
C[42] = 42
C[2] = 2;
const bar = C[42]

//// [staticIndexSignature2.js]
var C = /** @class */ (function () {
function C() {
}
return C;
}());
C["foo"] = 1;
C.bar = 2;
var foo = C["foo"];
C[42] = 42;
C[2] = 2;
var bar = C[42];
31 changes: 31 additions & 0 deletions tests/baselines/reference/staticIndexSignature2.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
=== tests/cases/conformance/classes/staticIndexSignature/staticIndexSignature2.ts ===
class C {
>C : Symbol(C, Decl(staticIndexSignature2.ts, 0, 0))

static readonly [s: string]: number;
>s : Symbol(s, Decl(staticIndexSignature2.ts, 1, 21))

static readonly [s: number]: 42
>s : Symbol(s, Decl(staticIndexSignature2.ts, 2, 21))
}

C["foo"] = 1
>C : Symbol(C, Decl(staticIndexSignature2.ts, 0, 0))

C.bar = 2;
>C : Symbol(C, Decl(staticIndexSignature2.ts, 0, 0))

const foo = C["foo"]
>foo : Symbol(foo, Decl(staticIndexSignature2.ts, 7, 5))
>C : Symbol(C, Decl(staticIndexSignature2.ts, 0, 0))

C[42] = 42
>C : Symbol(C, Decl(staticIndexSignature2.ts, 0, 0))

C[2] = 2;
>C : Symbol(C, Decl(staticIndexSignature2.ts, 0, 0))

const bar = C[42]
>bar : Symbol(bar, Decl(staticIndexSignature2.ts, 10, 5))
>C : Symbol(C, Decl(staticIndexSignature2.ts, 0, 0))

Loading

0 comments on commit 41dc625

Please sign in to comment.