Skip to content

Commit

Permalink
Support interpreting non-literal computed properties in classes as im…
Browse files Browse the repository at this point in the history
…plicit index signatures (#59860)
  • Loading branch information
weswigham authored Sep 23, 2024
1 parent e24cc01 commit fa0080f
Show file tree
Hide file tree
Showing 34 changed files with 400 additions and 81 deletions.
178 changes: 148 additions & 30 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1167,6 +1167,7 @@ export const notImplementedResolver: EmitResolver = {
getDeclarationStatementsForSourceFile: notImplemented,
isImportRequiredByAugmentation: notImplemented,
isDefinitelyReferenceToGlobalSymbolObject: notImplemented,
createLateBoundIndexSignatures: notImplemented,
};

const enum PipelinePhase {
Expand Down
4 changes: 3 additions & 1 deletion src/compiler/transformers/declarations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
canHaveModifiers,
canProduceDiagnostics,
ClassDeclaration,
ClassElement,
compact,
concatenate,
ConditionalTypeNode,
Expand Down Expand Up @@ -1652,7 +1653,8 @@ export function transformDeclarations(context: TransformationContext): Transform
/*initializer*/ undefined,
),
] : undefined;
const memberNodes = concatenate(concatenate(privateIdentifier, parameterProperties), visitNodes(input.members, visitDeclarationSubtree, isClassElement));
const lateIndexes = resolver.createLateBoundIndexSignatures(input, enclosingDeclaration, declarationEmitNodeBuilderFlags, declarationEmitInternalNodeBuilderFlags, symbolTracker);
const memberNodes = concatenate(concatenate(concatenate<ClassElement>(privateIdentifier, lateIndexes), parameterProperties), visitNodes(input.members, visitDeclarationSubtree, isClassElement));
const members = factory.createNodeArray(memberNodes);

const extendsClause = getEffectiveBaseTypeNode(input);
Expand Down
3 changes: 2 additions & 1 deletion src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5048,7 +5048,7 @@ export interface TypeChecker {
/** @internal */ getTypeOfPropertyOfType(type: Type, propertyName: string): Type | undefined;
getIndexInfoOfType(type: Type, kind: IndexKind): IndexInfo | undefined;
getIndexInfosOfType(type: Type): readonly IndexInfo[];
getIndexInfosOfIndexSymbol: (indexSymbol: Symbol) => IndexInfo[];
getIndexInfosOfIndexSymbol: (indexSymbol: Symbol, siblingSymbols?: Symbol[] | undefined) => IndexInfo[];
getSignaturesOfType(type: Type, kind: SignatureKind): readonly Signature[];
getIndexTypeOfType(type: Type, kind: IndexKind): Type | undefined;
/** @internal */ getIndexType(type: Type): Type;
Expand Down Expand Up @@ -5867,6 +5867,7 @@ export interface EmitResolver {
getDeclarationStatementsForSourceFile(node: SourceFile, flags: NodeBuilderFlags, internalFlags: InternalNodeBuilderFlags, tracker: SymbolTracker): Statement[] | undefined;
isImportRequiredByAugmentation(decl: ImportDeclaration): boolean;
isDefinitelyReferenceToGlobalSymbolObject(node: Node): boolean;
createLateBoundIndexSignatures(cls: ClassLikeDeclaration, enclosingDeclaration: Node, flags: NodeBuilderFlags, internalFlags: InternalNodeBuilderFlags, tracker: SymbolTracker): IndexSignatureDeclaration[] | undefined;
}

// dprint-ignore
Expand Down
8 changes: 4 additions & 4 deletions tests/baselines/reference/ES5SymbolProperty2.types
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ module M {
> : ^^^
}
(new C)[Symbol.iterator];
>(new C)[Symbol.iterator] : any
> : ^^^
>(new C)[Symbol.iterator] : () => void
> : ^^^^^^^^^^
>(new C) : C
> : ^
>new C : C
Expand All @@ -41,8 +41,8 @@ module M {
}

(new M.C)[Symbol.iterator];
>(new M.C)[Symbol.iterator] : any
> : ^^^
>(new M.C)[Symbol.iterator] : () => void
> : ^^^^^^^^^^
>(new M.C) : M.C
> : ^^^
>new M.C : M.C
Expand Down
3 changes: 2 additions & 1 deletion tests/baselines/reference/ES5SymbolProperty3.types
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ class C {
}

(new C)[Symbol.iterator]
>(new C)[Symbol.iterator] : error
>(new C)[Symbol.iterator] : () => void
> : ^^^^^^^^^^
>(new C) : C
> : ^
>new C : C
Expand Down
3 changes: 2 additions & 1 deletion tests/baselines/reference/ES5SymbolProperty4.types
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ class C {
}

(new C)[Symbol.iterator]
>(new C)[Symbol.iterator] : error
>(new C)[Symbol.iterator] : () => void
> : ^^^^^^^^^^
>(new C) : C
> : ^
>new C : C
Expand Down
13 changes: 13 additions & 0 deletions tests/baselines/reference/ES5SymbolProperty5.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
ES5SymbolProperty5.ts(7,26): error TS2554: Expected 0 arguments, but got 1.


==== ES5SymbolProperty5.ts (1 errors) ====
var Symbol: { iterator: symbol };

class C {
[Symbol.iterator]() { }
}

(new C)[Symbol.iterator](0) // Should error
~
!!! error TS2554: Expected 0 arguments, but got 1.
6 changes: 4 additions & 2 deletions tests/baselines/reference/ES5SymbolProperty5.types
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ class C {
}

(new C)[Symbol.iterator](0) // Should error
>(new C)[Symbol.iterator](0) : error
>(new C)[Symbol.iterator] : error
>(new C)[Symbol.iterator](0) : void
> : ^^^^
>(new C)[Symbol.iterator] : () => void
> : ^^^^^^^^^^
>(new C) : C
> : ^
>new C : C
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/ES5SymbolProperty6.types
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ class C {
}

(new C)[Symbol.iterator]
>(new C)[Symbol.iterator] : any
> : ^^^
>(new C)[Symbol.iterator] : () => void
> : ^^^^^^^^^^
>(new C) : C
> : ^
>new C : C
Expand Down
3 changes: 2 additions & 1 deletion tests/baselines/reference/ES5SymbolProperty7.types
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ class C {
}

(new C)[Symbol.iterator]
>(new C)[Symbol.iterator] : error
>(new C)[Symbol.iterator] : () => void
> : ^^^^^^^^^^
>(new C) : C
> : ^
>new C : C
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6140,7 +6140,7 @@ declare namespace ts {
getPrivateIdentifierPropertyOfType(leftType: Type, name: string, location: Node): Symbol | undefined;
getIndexInfoOfType(type: Type, kind: IndexKind): IndexInfo | undefined;
getIndexInfosOfType(type: Type): readonly IndexInfo[];
getIndexInfosOfIndexSymbol: (indexSymbol: Symbol) => IndexInfo[];
getIndexInfosOfIndexSymbol: (indexSymbol: Symbol, siblingSymbols?: Symbol[] | undefined) => IndexInfo[];
getSignaturesOfType(type: Type, kind: SignatureKind): readonly Signature[];
getIndexTypeOfType(type: Type, kind: IndexKind): Type | undefined;
getBaseTypes(type: InterfaceType): BaseType[];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//// [tests/cases/compiler/classNonUniqueSymbolMethodHasSymbolIndexer.ts] ////

//// [classNonUniqueSymbolMethodHasSymbolIndexer.ts]
declare const a: symbol;
export class A {
[a]() { return 1 };
}
declare const e1: A[typeof a]; // no error, `A` has `symbol` index

type Constructor = new (...args: any[]) => {};
declare function Mix<T extends Constructor>(classish: T): T & (new (...args: any[]) => {mixed: true});

export const Mixer = Mix(class {
[a]() { return 1 };
});


//// [classNonUniqueSymbolMethodHasSymbolIndexer.js]
export class A {
[a]() { return 1; }
;
}
export const Mixer = Mix(class {
[a]() { return 1; }
;
});


//// [classNonUniqueSymbolMethodHasSymbolIndexer.d.ts]
export declare class A {
[x: symbol]: () => number;
}
export declare const Mixer: {
new (): {
[x: symbol]: () => number;
};
} & (new (...args: any[]) => {
mixed: true;
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//// [tests/cases/compiler/classNonUniqueSymbolMethodHasSymbolIndexer.ts] ////

=== classNonUniqueSymbolMethodHasSymbolIndexer.ts ===
declare const a: symbol;
>a : Symbol(a, Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 0, 13))

export class A {
>A : Symbol(A, Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 0, 24))

[a]() { return 1 };
>[a] : Symbol(A[a], Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 1, 16))
>a : Symbol(a, Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 0, 13))
}
declare const e1: A[typeof a]; // no error, `A` has `symbol` index
>e1 : Symbol(e1, Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 4, 13))
>A : Symbol(A, Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 0, 24))
>a : Symbol(a, Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 0, 13))

type Constructor = new (...args: any[]) => {};
>Constructor : Symbol(Constructor, Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 4, 30))
>args : Symbol(args, Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 6, 24))

declare function Mix<T extends Constructor>(classish: T): T & (new (...args: any[]) => {mixed: true});
>Mix : Symbol(Mix, Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 6, 46))
>T : Symbol(T, Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 7, 21))
>Constructor : Symbol(Constructor, Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 4, 30))
>classish : Symbol(classish, Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 7, 44))
>T : Symbol(T, Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 7, 21))
>T : Symbol(T, Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 7, 21))
>args : Symbol(args, Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 7, 68))
>mixed : Symbol(mixed, Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 7, 88))

export const Mixer = Mix(class {
>Mixer : Symbol(Mixer, Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 9, 12))
>Mix : Symbol(Mix, Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 6, 46))

[a]() { return 1 };
>[a] : Symbol((Anonymous class)[a], Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 9, 32))
>a : Symbol(a, Decl(classNonUniqueSymbolMethodHasSymbolIndexer.ts, 0, 13))

});

Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//// [tests/cases/compiler/classNonUniqueSymbolMethodHasSymbolIndexer.ts] ////

=== classNonUniqueSymbolMethodHasSymbolIndexer.ts ===
declare const a: symbol;
>a : symbol
> : ^^^^^^

export class A {
>A : A
> : ^

[a]() { return 1 };
>[a] : () => number
> : ^^^^^^^^^^^^
>a : symbol
> : ^^^^^^
>1 : 1
> : ^
}
declare const e1: A[typeof a]; // no error, `A` has `symbol` index
>e1 : () => number
> : ^^^^^^^^^^^^
>a : symbol
> : ^^^^^^

type Constructor = new (...args: any[]) => {};
>Constructor : Constructor
> : ^^^^^^^^^^^
>args : any[]
> : ^^^^^

declare function Mix<T extends Constructor>(classish: T): T & (new (...args: any[]) => {mixed: true});
>Mix : <T extends Constructor>(classish: T) => T & (new (...args: any[]) => { mixed: true; })
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^
>classish : T
> : ^
>args : any[]
> : ^^^^^
>mixed : true
> : ^^^^
>true : true
> : ^^^^

export const Mixer = Mix(class {
>Mixer : typeof (Anonymous class) & (new (...args: any[]) => { mixed: true; })
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^^^^ ^
>Mix(class { [a]() { return 1 };}) : typeof (Anonymous class) & (new (...args: any[]) => { mixed: true; })
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^^^^ ^
>Mix : <T extends Constructor>(classish: T) => T & (new (...args: any[]) => { mixed: true; })
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^
>class { [a]() { return 1 };} : typeof (Anonymous class)
> : ^^^^^^^^^^^^^^^^^^^^^^^^

[a]() { return 1 };
>[a] : () => number
> : ^^^^^^^^^^^^
>a : symbol
> : ^^^^^^
>1 : 1
> : ^

});

4 changes: 2 additions & 2 deletions tests/baselines/reference/complicatedPrivacy.types
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ module m1 {
export function f4(arg1:
>f4 : (arg1: { [number]: C1; }) => void
> : ^ ^^ ^^^^^^^^^
>arg1 : {}
> : ^^
>arg1 : { [x: number]: C1; }
> : ^^^^^^^^^^^^^^^^^^^^
{
[number]: C1; // Used to be indexer, now it is a computed property
>[number] : C1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ computedPropertyNames12_ES5.ts(6,5): error TS1166: A computed property name in a
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.
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.
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.
computedPropertyNames12_ES5.ts(9,5): error TS2411: Property '[+s]' of type 'string' is not assignable to 'number' index type 'number'.
computedPropertyNames12_ES5.ts(9,5): error TS2411: Property '[+s]' of type 'string' is not assignable to 'string' index type 'number'.
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.
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.
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.


==== computedPropertyNames12_ES5.ts (8 errors) ====
==== computedPropertyNames12_ES5.ts (10 errors) ====
var s: string;
var n: number;
var a: any;
Expand All @@ -28,6 +30,10 @@ computedPropertyNames12_ES5.ts(15,12): error TS1166: A computed property name in
[+s]: typeof s;
~~~~
!!! error TS1166: A computed property name in a class property declaration must have a simple literal type or a 'unique symbol' type.
~~~~
!!! error TS2411: Property '[+s]' of type 'string' is not assignable to 'number' index type 'number'.
~~~~
!!! error TS2411: Property '[+s]' of type 'string' is not assignable to 'string' index type 'number'.
static [""]: number;
[0]: number;
[a]: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ computedPropertyNames12_ES6.ts(6,5): error TS1166: A computed property name in a
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.
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.
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.
computedPropertyNames12_ES6.ts(9,5): error TS2411: Property '[+s]' of type 'string' is not assignable to 'number' index type 'number'.
computedPropertyNames12_ES6.ts(9,5): error TS2411: Property '[+s]' of type 'string' is not assignable to 'string' index type 'number'.
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.
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.
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.


==== computedPropertyNames12_ES6.ts (8 errors) ====
==== computedPropertyNames12_ES6.ts (10 errors) ====
var s: string;
var n: number;
var a: any;
Expand All @@ -28,6 +30,10 @@ computedPropertyNames12_ES6.ts(15,12): error TS1166: A computed property name in
[+s]: typeof s;
~~~~
!!! error TS1166: A computed property name in a class property declaration must have a simple literal type or a 'unique symbol' type.
~~~~
!!! error TS2411: Property '[+s]' of type 'string' is not assignable to 'number' index type 'number'.
~~~~
!!! error TS2411: Property '[+s]' of type 'string' is not assignable to 'string' index type 'number'.
static [""]: number;
[0]: number;
[a]: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ exports.C = C;

//// [main.d.ts]
export declare class C {
[x: number]: () => void;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
export type Type = { x?: { [Enum.A]: 0 } };
>Type : Type
> : ^^^^
>x : {} | undefined
> : ^^^^^^^^^^^^^^
>x : { [x: number]: 0; } | undefined
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>[Enum.A] : 0
> : ^
>Enum.A : any
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/parserComputedPropertyName13.types
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

=== parserComputedPropertyName13.ts ===
var v: { [e]: number };
>v : {}
> : ^^
>v : { [x: number]: number; }
> : ^^^^^^^^^^^^^^^^^^^^^^^^
>[e] : number
> : ^^^^^^
>e : any
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/parserComputedPropertyName14.types
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

=== parserComputedPropertyName14.ts ===
var v: { [e](): number };
>v : {}
> : ^^
>v : { [x: number]: () => number; }
> : ^^^^^^^^^^^^^^^^^^^^^ ^^^
>[e] : () => number
> : ^^^^^^
>e : any
Expand Down
Loading

0 comments on commit fa0080f

Please sign in to comment.