From 3298a5ece90a163e595696bc755de6a907669790 Mon Sep 17 00:00:00 2001 From: hayatexe Date: Sun, 22 Feb 2026 01:01:27 +0300 Subject: [PATCH] fix(25083): allow element access expressions with string or numeric literal arguments as computed properties --- src/compiler/checker.ts | 14 +++++++-- .../reference/computedPropertyWithEnumKey.js | 13 ++++++++ .../computedPropertyWithEnumKey.symbols | 16 ++++++++++ .../computedPropertyWithEnumKey.types | 31 +++++++++++++++++++ .../isolatedDeclarationLazySymbols.errors.txt | 6 ++-- .../isolatedDeclarationLazySymbols.types | 4 +-- .../compiler/computedPropertyWithEnumKey.ts | 1 + 7 files changed, 78 insertions(+), 7 deletions(-) create mode 100644 tests/baselines/reference/computedPropertyWithEnumKey.js create mode 100644 tests/baselines/reference/computedPropertyWithEnumKey.symbols create mode 100644 tests/baselines/reference/computedPropertyWithEnumKey.types create mode 100644 tests/cases/compiler/computedPropertyWithEnumKey.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8fa3c4fce2a4a..4ff85eb127afa 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13785,12 +13785,22 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { && isTypeUsableAsIndexSignature(isComputedPropertyName(node) ? checkComputedPropertyName(node) : checkExpressionCached((node as ElementAccessExpression).argumentExpression)); } + function isEntityNameExpressionOrElementAccess(node: Node): boolean { + if (isEntityNameExpression(node)) { + return true; + } + if (isElementAccessExpression(node) && isStringOrNumericLiteralLike(skipParentheses(node.argumentExpression))) { + return isEntityNameExpressionOrElementAccess(node.expression); + } + return false; + } + function isLateBindableAST(node: DeclarationName) { if (!isComputedPropertyName(node) && !isElementAccessExpression(node)) { return false; } const expr = isComputedPropertyName(node) ? node.expression : node.argumentExpression; - return isEntityNameExpression(expr); + return isEntityNameExpressionOrElementAccess(expr); } function isTypeUsableAsIndexSignature(type: Type): boolean { @@ -52956,7 +52966,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function checkGrammarForInvalidDynamicName(node: DeclarationName, message: DiagnosticMessage) { // Even non-bindable names are allowed as late-bound implied index signatures so long as the name is a simple `a.b.c` type name expression - if (isNonBindableDynamicName(node) && !isEntityNameExpression(isElementAccessExpression(node) ? skipParentheses(node.argumentExpression) : (node as ComputedPropertyName).expression)) { + if (isNonBindableDynamicName(node) && !isEntityNameExpressionOrElementAccess(isElementAccessExpression(node) ? skipParentheses(node.argumentExpression) : (node as ComputedPropertyName).expression)) { return grammarErrorOnNode(node, message); } } diff --git a/tests/baselines/reference/computedPropertyWithEnumKey.js b/tests/baselines/reference/computedPropertyWithEnumKey.js new file mode 100644 index 0000000000000..cadf4665d232b --- /dev/null +++ b/tests/baselines/reference/computedPropertyWithEnumKey.js @@ -0,0 +1,13 @@ +//// [tests/cases/compiler/computedPropertyWithEnumKey.ts] //// + +//// [computedPropertyWithEnumKey.ts] +enum Type { Foo = 'foo', '3x14' = '3.14' } type TypeMap = { [Type.Foo]: any; [Type['3x14']]: any; }; + + +//// [computedPropertyWithEnumKey.js] +"use strict"; +var Type; +(function (Type) { + Type["Foo"] = "foo"; + Type["3x14"] = "3.14"; +})(Type || (Type = {})); diff --git a/tests/baselines/reference/computedPropertyWithEnumKey.symbols b/tests/baselines/reference/computedPropertyWithEnumKey.symbols new file mode 100644 index 0000000000000..8d36d7028a2fc --- /dev/null +++ b/tests/baselines/reference/computedPropertyWithEnumKey.symbols @@ -0,0 +1,16 @@ +//// [tests/cases/compiler/computedPropertyWithEnumKey.ts] //// + +=== computedPropertyWithEnumKey.ts === +enum Type { Foo = 'foo', '3x14' = '3.14' } type TypeMap = { [Type.Foo]: any; [Type['3x14']]: any; }; +>Type : Symbol(Type, Decl(computedPropertyWithEnumKey.ts, 0, 0)) +>Foo : Symbol(Type.Foo, Decl(computedPropertyWithEnumKey.ts, 0, 11)) +>'3x14' : Symbol(Type['3x14'], Decl(computedPropertyWithEnumKey.ts, 0, 24)) +>TypeMap : Symbol(TypeMap, Decl(computedPropertyWithEnumKey.ts, 0, 42)) +>[Type.Foo] : Symbol([Type.Foo], Decl(computedPropertyWithEnumKey.ts, 0, 59)) +>Type.Foo : Symbol(Type.Foo, Decl(computedPropertyWithEnumKey.ts, 0, 11)) +>Type : Symbol(Type, Decl(computedPropertyWithEnumKey.ts, 0, 0)) +>Foo : Symbol(Type.Foo, Decl(computedPropertyWithEnumKey.ts, 0, 11)) +>[Type['3x14']] : Symbol([Type['3x14']], Decl(computedPropertyWithEnumKey.ts, 0, 76)) +>Type : Symbol(Type, Decl(computedPropertyWithEnumKey.ts, 0, 0)) +>'3x14' : Symbol(Type['3x14'], Decl(computedPropertyWithEnumKey.ts, 0, 24)) + diff --git a/tests/baselines/reference/computedPropertyWithEnumKey.types b/tests/baselines/reference/computedPropertyWithEnumKey.types new file mode 100644 index 0000000000000..81e8832897666 --- /dev/null +++ b/tests/baselines/reference/computedPropertyWithEnumKey.types @@ -0,0 +1,31 @@ +//// [tests/cases/compiler/computedPropertyWithEnumKey.ts] //// + +=== computedPropertyWithEnumKey.ts === +enum Type { Foo = 'foo', '3x14' = '3.14' } type TypeMap = { [Type.Foo]: any; [Type['3x14']]: any; }; +>Type : Type +> : ^^^^ +>Foo : Type.Foo +> : ^^^^^^^^ +>'foo' : "foo" +> : ^^^^^ +>'3x14' : (typeof Type)["3x14"] +> : ^^^^^^^^^^^^^^^^^^^^^ +>'3.14' : "3.14" +> : ^^^^^^ +>TypeMap : TypeMap +> : ^^^^^^^ +>[Type.Foo] : any +>Type.Foo : Type.Foo +> : ^^^^^^^^ +>Type : typeof Type +> : ^^^^^^^^^^^ +>Foo : Type.Foo +> : ^^^^^^^^ +>[Type['3x14']] : any +>Type['3x14'] : (typeof Type)["3x14"] +> : ^^^^^^^^^^^^^^^^^^^^^ +>Type : typeof Type +> : ^^^^^^^^^^^ +>'3x14' : "3x14" +> : ^^^^^^ + diff --git a/tests/baselines/reference/isolatedDeclarationLazySymbols.errors.txt b/tests/baselines/reference/isolatedDeclarationLazySymbols.errors.txt index 812ec2c1bea83..6931ab5f5df2b 100644 --- a/tests/baselines/reference/isolatedDeclarationLazySymbols.errors.txt +++ b/tests/baselines/reference/isolatedDeclarationLazySymbols.errors.txt @@ -1,6 +1,6 @@ isolatedDeclarationLazySymbols.ts(1,17): error TS9007: Function must have an explicit return type annotation with --isolatedDeclarations. +isolatedDeclarationLazySymbols.ts(12,1): error TS9023: Assigning properties to functions without declaring them is not supported with --isolatedDeclarations. Add an explicit declaration for the properties assigned to this function. isolatedDeclarationLazySymbols.ts(13,1): error TS9023: Assigning properties to functions without declaring them is not supported with --isolatedDeclarations. Add an explicit declaration for the properties assigned to this function. -isolatedDeclarationLazySymbols.ts(16,5): error TS1166: A computed property name in a class property declaration must have a simple literal type or a 'unique symbol' type. isolatedDeclarationLazySymbols.ts(16,5): error TS9038: Computed property names on class or object literals cannot be inferred with --isolatedDeclarations. isolatedDeclarationLazySymbols.ts(21,5): error TS9038: Computed property names on class or object literals cannot be inferred with --isolatedDeclarations. isolatedDeclarationLazySymbols.ts(22,5): error TS9038: Computed property names on class or object literals cannot be inferred with --isolatedDeclarations. @@ -22,6 +22,8 @@ isolatedDeclarationLazySymbols.ts(22,5): error TS9038: Computed property names o } as const foo[o["prop.inner"]] ="A"; + ~~~~~~~~~~~~~~~~~~~~ +!!! error TS9023: Assigning properties to functions without declaring them is not supported with --isolatedDeclarations. Add an explicit declaration for the properties assigned to this function. foo[o.prop.inner] = "B"; ~~~~~~~~~~~~~~~~~ !!! error TS9023: Assigning properties to functions without declaring them is not supported with --isolatedDeclarations. Add an explicit declaration for the properties assigned to this function. @@ -29,8 +31,6 @@ isolatedDeclarationLazySymbols.ts(22,5): error TS9038: Computed property names o export class Foo { [o["prop.inner"]] ="A" ~~~~~~~~~~~~~~~~~ -!!! error TS1166: A computed property name in a class property declaration must have a simple literal type or a 'unique symbol' type. - ~~~~~~~~~~~~~~~~~ !!! error TS9038: Computed property names on class or object literals cannot be inferred with --isolatedDeclarations. [o.prop.inner] = "B" } diff --git a/tests/baselines/reference/isolatedDeclarationLazySymbols.types b/tests/baselines/reference/isolatedDeclarationLazySymbols.types index c7e91f9680a8c..5a1bfe22638bd 100644 --- a/tests/baselines/reference/isolatedDeclarationLazySymbols.types +++ b/tests/baselines/reference/isolatedDeclarationLazySymbols.types @@ -40,8 +40,8 @@ const o = { foo[o["prop.inner"]] ="A"; >foo[o["prop.inner"]] ="A" : "A" > : ^^^ ->foo[o["prop.inner"]] : any -> : ^^^ +>foo[o["prop.inner"]] : string +> : ^^^^^^ >foo : typeof foo > : ^^^^^^^^^^ >o["prop.inner"] : "a" diff --git a/tests/cases/compiler/computedPropertyWithEnumKey.ts b/tests/cases/compiler/computedPropertyWithEnumKey.ts new file mode 100644 index 0000000000000..53447154f85a4 --- /dev/null +++ b/tests/cases/compiler/computedPropertyWithEnumKey.ts @@ -0,0 +1 @@ +enum Type { Foo = 'foo', '3x14' = '3.14' } type TypeMap = { [Type.Foo]: any; [Type['3x14']]: any; };