Support interpreting non-literal computed properties in classes as implicit index signatures (#59860)

This commit is contained in:
Wesley Wigham 2024-09-23 11:09:06 -07:00 committed by GitHub
parent e24cc01b08
commit fa0080f480
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
34 changed files with 400 additions and 81 deletions

View File

@ -583,6 +583,7 @@ import {
isImportTypeNode,
isInCompoundLikeAssignment,
isIndexedAccessTypeNode,
isIndexSignatureDeclaration,
isInExpressionContext,
isInfinityOrNaNString,
isInitializedProperty,
@ -878,6 +879,7 @@ import {
ModuleResolutionKind,
ModuleSpecifierResolutionHost,
Mutable,
MutableNodeArray,
NamedDeclaration,
NamedExports,
NamedImportsOrExports,
@ -13345,12 +13347,25 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
* - The type of its expression is a string or numeric literal type, or is a `unique symbol` type.
*/
function isLateBindableName(node: DeclarationName): node is LateBoundName {
return isLateBindableAST(node)
&& isTypeUsableAsPropertyName(isComputedPropertyName(node) ? checkComputedPropertyName(node) : checkExpressionCached((node as ElementAccessExpression).argumentExpression));
}
function isLateBindableIndexSignature(node: DeclarationName): node is LateBoundName {
return isLateBindableAST(node)
&& isTypeUsableAsIndexSignature(isComputedPropertyName(node) ? checkComputedPropertyName(node) : checkExpressionCached((node as ElementAccessExpression).argumentExpression));
}
function isLateBindableAST(node: DeclarationName) {
if (!isComputedPropertyName(node) && !isElementAccessExpression(node)) {
return false;
}
const expr = isComputedPropertyName(node) ? node.expression : node.argumentExpression;
return isEntityNameExpression(expr)
&& isTypeUsableAsPropertyName(isComputedPropertyName(node) ? checkComputedPropertyName(node) : checkExpressionCached(expr));
return isEntityNameExpression(expr);
}
function isTypeUsableAsIndexSignature(type: Type): boolean {
return isTypeAssignableTo(type, stringNumberSymbolType);
}
function isLateBoundName(name: __String): boolean {
@ -13367,6 +13382,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return !!name && isLateBindableName(name);
}
function hasLateBindableIndexSignature(node: Declaration) {
const name = getNameOfDeclaration(node);
return !!name && isLateBindableIndexSignature(name);
}
/**
* Indicates whether a declaration has an early-bound name or a dynamic name that can be late-bound.
*/
@ -13474,6 +13494,31 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return links.resolvedSymbol;
}
function lateBindIndexSignature(parent: Symbol, earlySymbols: SymbolTable | undefined, lateSymbols: Map<__String, TransientSymbol>, decl: LateBoundDeclaration | LateBoundBinaryExpressionDeclaration) {
// First, late bind the index symbol itself, if needed
let indexSymbol = lateSymbols.get(InternalSymbolName.Index);
if (!indexSymbol) {
const early = earlySymbols?.get(InternalSymbolName.Index);
if (!early) {
indexSymbol = createSymbol(SymbolFlags.None, InternalSymbolName.Index, CheckFlags.Late);
}
else {
indexSymbol = cloneSymbol(early);
indexSymbol.links.checkFlags |= CheckFlags.Late;
}
lateSymbols.set(InternalSymbolName.Index, indexSymbol);
}
// Then just add the computed name as a late bound declaration
// (note: unlike `addDeclarationToLateBoundSymbol` we do not set up a `.lateSymbol` on `decl`'s links,
// since that would point at an index symbol and not a single property symbol, like most consumers would expect)
if (!indexSymbol.declarations) {
indexSymbol.declarations = [decl];
}
else if (!decl.symbol.isReplaceableByMethod) {
indexSymbol.declarations.push(decl);
}
}
function getResolvedMembersOrExportsOfSymbol(symbol: Symbol, resolutionKind: MembersOrExportsResolutionKind): Map<__String, Symbol> {
const links = getSymbolLinks(symbol);
if (!links[resolutionKind]) {
@ -13497,6 +13542,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
if (hasLateBindableName(member)) {
lateBindMember(symbol, earlySymbols, lateSymbols, member);
}
else if (hasLateBindableIndexSignature(member)) {
lateBindIndexSignature(symbol, earlySymbols, lateSymbols, member as Node as LateBoundDeclaration | LateBoundBinaryExpressionDeclaration);
}
}
}
}
@ -14160,7 +14208,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const indexSymbol = getIndexSymbolFromSymbolTable(members);
if (indexSymbol) {
indexInfos = getIndexInfosOfIndexSymbol(indexSymbol);
indexInfos = getIndexInfosOfIndexSymbol(indexSymbol, arrayFrom(members.values()));
}
else {
if (baseConstructorIndexInfo) {
@ -16192,7 +16240,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
function getIndexSymbol(symbol: Symbol): Symbol | undefined {
return symbol.members ? getIndexSymbolFromSymbolTable(symbol.members) : undefined;
return symbol.members ? getIndexSymbolFromSymbolTable(getMembersOfSymbol(symbol)) : undefined;
}
function getIndexSymbolFromSymbolTable(symbolTable: SymbolTable): Symbol | undefined {
@ -16205,24 +16253,67 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
function getIndexInfosOfSymbol(symbol: Symbol): IndexInfo[] {
const indexSymbol = getIndexSymbol(symbol);
return indexSymbol ? getIndexInfosOfIndexSymbol(indexSymbol) : emptyArray;
return indexSymbol ? getIndexInfosOfIndexSymbol(indexSymbol, arrayFrom(getMembersOfSymbol(symbol).values())) : emptyArray;
}
function getIndexInfosOfIndexSymbol(indexSymbol: Symbol): IndexInfo[] {
// note intentional similarities to index signature building in `checkObjectLiteral` for parity
function getIndexInfosOfIndexSymbol(indexSymbol: Symbol, siblingSymbols: Symbol[] | undefined = indexSymbol.parent ? arrayFrom(getMembersOfSymbol(indexSymbol.parent).values()) : undefined): IndexInfo[] {
if (indexSymbol.declarations) {
const indexInfos: IndexInfo[] = [];
for (const declaration of (indexSymbol.declarations as IndexSignatureDeclaration[])) {
if (declaration.parameters.length === 1) {
const parameter = declaration.parameters[0];
if (parameter.type) {
forEachType(getTypeFromTypeNode(parameter.type), keyType => {
if (isValidIndexKeyType(keyType) && !findIndexInfo(indexInfos, keyType)) {
indexInfos.push(createIndexInfo(keyType, declaration.type ? getTypeFromTypeNode(declaration.type) : anyType, hasEffectiveModifier(declaration, ModifierFlags.Readonly), declaration));
let hasComputedNumberProperty = false;
let readonlyComputedNumberProperty = true;
let hasComputedSymbolProperty = false;
let readonlyComputedSymbolProperty = true;
let hasComputedStringProperty = false;
let readonlyComputedStringProperty = true;
const computedPropertySymbols: Symbol[] = [];
for (const declaration of indexSymbol.declarations) {
if (isIndexSignatureDeclaration(declaration)) {
if (declaration.parameters.length === 1) {
const parameter = declaration.parameters[0];
if (parameter.type) {
forEachType(getTypeFromTypeNode(parameter.type), keyType => {
if (isValidIndexKeyType(keyType) && !findIndexInfo(indexInfos, keyType)) {
indexInfos.push(createIndexInfo(keyType, declaration.type ? getTypeFromTypeNode(declaration.type) : anyType, hasEffectiveModifier(declaration, ModifierFlags.Readonly), declaration));
}
});
}
}
}
else if (hasLateBindableIndexSignature(declaration)) {
const declName = isBinaryExpression(declaration) ? declaration.left as ElementAccessExpression : (declaration as LateBoundDeclaration).name;
const keyType = isElementAccessExpression(declName) ? checkExpressionCached(declName.argumentExpression) : checkComputedPropertyName(declName);
if (findIndexInfo(indexInfos, keyType)) {
continue; // Explicit index for key type takes priority
}
if (isTypeAssignableTo(keyType, stringNumberSymbolType)) {
if (isTypeAssignableTo(keyType, numberType)) {
hasComputedNumberProperty = true;
if (!hasEffectiveReadonlyModifier(declaration)) {
readonlyComputedNumberProperty = false;
}
});
}
else if (isTypeAssignableTo(keyType, esSymbolType)) {
hasComputedSymbolProperty = true;
if (!hasEffectiveReadonlyModifier(declaration)) {
readonlyComputedSymbolProperty = false;
}
}
else {
hasComputedStringProperty = true;
if (!hasEffectiveReadonlyModifier(declaration)) {
readonlyComputedStringProperty = false;
}
}
computedPropertySymbols.push(declaration.symbol);
}
}
}
const allPropertySymbols = concatenate(computedPropertySymbols, filter(siblingSymbols, s => s !== indexSymbol));
// aggregate similar index infos implied to be the same key to the same combined index info
if (hasComputedStringProperty && !findIndexInfo(indexInfos, stringType)) indexInfos.push(getObjectLiteralIndexInfo(readonlyComputedStringProperty, 0, allPropertySymbols, stringType));
if (hasComputedNumberProperty && !findIndexInfo(indexInfos, numberType)) indexInfos.push(getObjectLiteralIndexInfo(readonlyComputedNumberProperty, 0, allPropertySymbols, numberType));
if (hasComputedSymbolProperty && !findIndexInfo(indexInfos, esSymbolType)) indexInfos.push(getObjectLiteralIndexInfo(readonlyComputedSymbolProperty, 0, allPropertySymbols, esSymbolType));
return indexInfos;
}
return emptyArray;
@ -32790,7 +32881,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
isTypeAssignableToKind(checkComputedPropertyName(firstDecl.name), TypeFlags.ESSymbol));
}
function getObjectLiteralIndexInfo(node: ObjectLiteralExpression, offset: number, properties: Symbol[], keyType: Type): IndexInfo {
// NOTE: currently does not make pattern literal indexers, eg `${number}px`
function getObjectLiteralIndexInfo(isReadonly: boolean, offset: number, properties: Symbol[], keyType: Type): IndexInfo {
const propTypes: Type[] = [];
for (let i = offset; i < properties.length; i++) {
const prop = properties[i];
@ -32803,7 +32895,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
}
const unionType = propTypes.length ? getUnionType(propTypes, UnionReduction.Subtype) : undefinedType;
return createIndexInfo(keyType, unionType, isConstContext(node));
return createIndexInfo(keyType, unionType, isReadonly);
}
function getImmediateAliasedSymbol(symbol: Symbol): Symbol | undefined {
@ -33008,9 +33100,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
function createObjectLiteralType() {
const indexInfos = [];
if (hasComputedStringProperty) indexInfos.push(getObjectLiteralIndexInfo(node, offset, propertiesArray, stringType));
if (hasComputedNumberProperty) indexInfos.push(getObjectLiteralIndexInfo(node, offset, propertiesArray, numberType));
if (hasComputedSymbolProperty) indexInfos.push(getObjectLiteralIndexInfo(node, offset, propertiesArray, esSymbolType));
const isReadonly = isConstContext(node);
if (hasComputedStringProperty) indexInfos.push(getObjectLiteralIndexInfo(isReadonly, offset, propertiesArray, stringType));
if (hasComputedNumberProperty) indexInfos.push(getObjectLiteralIndexInfo(isReadonly, offset, propertiesArray, numberType));
if (hasComputedSymbolProperty) indexInfos.push(getObjectLiteralIndexInfo(isReadonly, offset, propertiesArray, esSymbolType));
const result = createAnonymousType(node.symbol, propertiesTable, emptyArray, emptyArray, indexInfos);
result.objectFlags |= objectFlags | ObjectFlags.ObjectLiteral | ObjectFlags.ContainsObjectOrArrayLiteral;
if (isJSObjectLiteral) {
@ -41575,18 +41668,21 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const indexSymbol = getIndexSymbol(getSymbolOfDeclaration(node));
if (indexSymbol?.declarations) {
const indexSignatureMap = new Map<TypeId, { type: Type; declarations: IndexSignatureDeclaration[]; }>();
for (const declaration of (indexSymbol.declarations as IndexSignatureDeclaration[])) {
if (declaration.parameters.length === 1 && declaration.parameters[0].type) {
forEachType(getTypeFromTypeNode(declaration.parameters[0].type), type => {
const entry = indexSignatureMap.get(getTypeId(type));
if (entry) {
entry.declarations.push(declaration);
}
else {
indexSignatureMap.set(getTypeId(type), { type, declarations: [declaration] });
}
});
for (const declaration of indexSymbol.declarations) {
if (isIndexSignatureDeclaration(declaration)) {
if (declaration.parameters.length === 1 && declaration.parameters[0].type) {
forEachType(getTypeFromTypeNode(declaration.parameters[0].type), type => {
const entry = indexSignatureMap.get(getTypeId(type));
if (entry) {
entry.declarations.push(declaration);
}
else {
indexSignatureMap.set(getTypeId(type), { type, declarations: [declaration] });
}
});
}
}
// Do nothing for late-bound index signatures: allow these to duplicate one another and explicit indexes
}
indexSignatureMap.forEach(entry => {
if (entry.declarations.length > 1) {
@ -50433,6 +50529,28 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
},
isImportRequiredByAugmentation,
isDefinitelyReferenceToGlobalSymbolObject,
createLateBoundIndexSignatures: (cls, enclosing, flags, internalFlags, tracker) => {
const sym = cls.symbol;
const staticInfos = getIndexInfosOfType(getTypeOfSymbol(sym));
const instanceIndexSymbol = getIndexSymbol(sym);
const instanceInfos = instanceIndexSymbol && getIndexInfosOfIndexSymbol(instanceIndexSymbol, arrayFrom(getMembersOfSymbol(sym).values()));
let result;
for (const infoList of [staticInfos, instanceInfos]) {
if (!length(infoList)) continue;
result ||= [];
for (const info of infoList!) {
if (info.declaration) continue;
const node = nodeBuilder.indexInfoToIndexSignatureDeclaration(info, enclosing, flags, internalFlags, tracker);
if (node && infoList === staticInfos) {
(((node as Mutable<typeof node>).modifiers ||= factory.createNodeArray()) as MutableNodeArray<Modifier>).unshift(factory.createModifier(SyntaxKind.StaticKeyword));
}
if (node) {
result.push(node);
}
}
}
return result;
},
};
function isImportRequiredByAugmentation(node: ImportDeclaration) {

View File

@ -1167,6 +1167,7 @@ export const notImplementedResolver: EmitResolver = {
getDeclarationStatementsForSourceFile: notImplemented,
isImportRequiredByAugmentation: notImplemented,
isDefinitelyReferenceToGlobalSymbolObject: notImplemented,
createLateBoundIndexSignatures: notImplemented,
};
const enum PipelinePhase {

View File

@ -12,6 +12,7 @@ import {
canHaveModifiers,
canProduceDiagnostics,
ClassDeclaration,
ClassElement,
compact,
concatenate,
ConditionalTypeNode,
@ -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);

View File

@ -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;
@ -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

View File

@ -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
@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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[];

View File

@ -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;
});

View File

@ -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))
});

View File

@ -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
> : ^
});

View File

@ -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

View File

@ -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;
@ -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;

View File

@ -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;
@ -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;

View File

@ -28,4 +28,5 @@ exports.C = C;
//// [main.d.ts]
export declare class C {
[x: number]: () => void;
}

View File

@ -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

View File

@ -2,8 +2,8 @@
=== parserComputedPropertyName13.ts ===
var v: { [e]: number };
>v : {}
> : ^^
>v : { [x: number]: number; }
> : ^^^^^^^^^^^^^^^^^^^^^^^^
>[e] : number
> : ^^^^^^
>e : any

View File

@ -2,8 +2,8 @@
=== parserComputedPropertyName14.ts ===
var v: { [e](): number };
>v : {}
> : ^^
>v : { [x: number]: () => number; }
> : ^^^^^^^^^^^^^^^^^^^^^ ^^^
>[e] : () => number
> : ^^^^^^
>e : any

View File

@ -2,8 +2,8 @@
=== parserComputedPropertyName18.ts ===
var v: { [e]?(): number };
>v : {}
> : ^^
>v : { [x: number]: () => number; }
> : ^^^^^^^^^^^^^^^^^^^^^ ^^^
>[e] : () => number
> : ^^^^^^
>e : any

View File

@ -2,8 +2,8 @@
=== parserComputedPropertyName19.ts ===
var v: { [e]? };
>v : {}
> : ^^
>v : { [x: number]: any; }
> : ^^^^^^^^^^^^^^^^^^^^^
>[e] : any
> : ^^^
>e : any

View File

@ -2,8 +2,8 @@
=== parserES5ComputedPropertyName8.ts ===
var v: { [e]: number };
>v : {}
> : ^^
>v : { [x: number]: number; }
> : ^^^^^^^^^^^^^^^^^^^^^^^^
>[e] : number
> : ^^^^^^
>e : any

View File

@ -2,8 +2,8 @@
=== parserES5SymbolProperty8.ts ===
var x: {
>x : {}
> : ^^
>x : { [x: number]: () => string; }
> : ^^^^^^^^^^^^^^^^^^^^^ ^^^
[Symbol.toPrimitive](): string
>[Symbol.toPrimitive] : () => string

View File

@ -2,8 +2,8 @@
=== parserES5SymbolProperty9.ts ===
var x: {
>x : {}
> : ^^
>x : { [x: number]: string; }
> : ^^^^^^^^^^^^^^^^^^^^^^^^
[Symbol.toPrimitive]: string
>[Symbol.toPrimitive] : string

View File

@ -12,8 +12,8 @@ var bar1: { x : number; }
> : ^^^^^^
var foo2: { [index]; } // should be an error, used to be indexer, now it is a computed property
>foo2 : {}
> : ^^
>foo2 : { [x: number]: any; }
> : ^^^^^^^^^^^^^^^^^^^^^
>[index] : any
> : ^^^
>index : any
@ -48,8 +48,8 @@ foo1 = bar1; // should be an error
foo2 = bar2;
>foo2 = bar2 : { x: number; }
> : ^^^^^ ^^^
>foo2 : {}
> : ^^
>foo2 : { [x: number]: any; }
> : ^^^^^^^^^^^^^^^^^^^^^
>bar2 : { x: number; }
> : ^^^^^ ^^^

View File

@ -38,14 +38,16 @@ class Bar extends Foo {
> : ^^^
[symbol]() {
>[symbol] : () => any
> : ^^^^^^^^^
>[symbol] : () => number
> : ^^^^^^^^^^^^
>symbol : symbol
> : ^^^^^^
return super[symbol]();
>super[symbol]() : error
>super[symbol] : error
>super[symbol]() : number
> : ^^^^^^
>super[symbol] : () => number
> : ^^^^^^^^^^^^
>super : Foo
> : ^^^
>symbol : symbol

View File

@ -26,13 +26,15 @@ class Bar extends Foo {
> : ^^^
[symbol]() {
>[symbol] : () => any
> : ^^^^^^^^^
>[symbol] : () => number
> : ^^^^^^^^^^^^
>symbol : any
return super[symbol]();
>super[symbol]() : error
>super[symbol] : error
>super[symbol]() : number
> : ^^^^^^
>super[symbol] : () => number
> : ^^^^^^^^^^^^
>super : Foo
> : ^^^
>symbol : any

View File

@ -26,13 +26,15 @@ class Bar extends Foo {
> : ^^^
static [symbol]() {
>[symbol] : () => any
> : ^^^^^^^^^
>[symbol] : () => number
> : ^^^^^^^^^^^^
>symbol : any
return super[symbol]();
>super[symbol]() : error
>super[symbol] : error
>super[symbol]() : number
> : ^^^^^^
>super[symbol] : () => number
> : ^^^^^^^^^^^^
>super : typeof Foo
> : ^^^^^^^^^^
>symbol : any

View File

@ -86,6 +86,7 @@ export interface B {
["2"]: number;
}
export declare class C {
[x: number]: number;
[Symbol.iterator]: number;
[globalThis.Symbol.toStringTag]: number;
[1]: number;

View File

@ -0,0 +1,15 @@
// @target: es6
// @strict: true
// @declaration: true
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 };
});

View File

@ -2,4 +2,4 @@
//// function method() { var /**/dictionary = <{ [index]: string; }>{}; }
verify.quickInfoAt("", "(local var) dictionary: {}");
verify.quickInfoAt("", "(local var) dictionary: {\n [x: number]: string;\n}");