From e813bb07c3240782afc0634a81438d625577c990 Mon Sep 17 00:00:00 2001 From: Alessio Attilio Date: Fri, 9 Jan 2026 23:01:01 +0100 Subject: [PATCH 1/2] fix(checker): Allow element access expressions in computed property names if argument is literal --- src/compiler/checker.ts | 20 ++++++++++--- tests/cases/compiler/enumKeysInTypeLiteral.ts | 29 +++++++++++++++++++ 2 files changed, 45 insertions(+), 4 deletions(-) create mode 100644 tests/cases/compiler/enumKeysInTypeLiteral.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2523d3baaba55..5dc439aba20b3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -742,6 +742,7 @@ import { isShorthandAmbientModuleSymbol, isShorthandPropertyAssignment, isSideEffectImport, + isSignedNumericLiteral, isSingleOrDoubleQuote, isSourceFile, isSourceFileJS, @@ -13720,14 +13721,25 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { && isTypeUsableAsIndexSignature(isComputedPropertyName(node) ? checkComputedPropertyName(node) : checkExpressionCached((node as ElementAccessExpression).argumentExpression)); } - function isLateBindableAST(node: DeclarationName) { - if (!isComputedPropertyName(node) && !isElementAccessExpression(node)) { - return false; + function isLateBindableExpression(expr: Expression): boolean { + while (isElementAccessExpression(expr)) { + const argument = skipParentheses(expr.argumentExpression); + if (!isStringOrNumericLiteralLike(argument) && !isSignedNumericLiteral(argument)) return false; + expr = expr.expression; } - const expr = isComputedPropertyName(node) ? node.expression : node.argumentExpression; return isEntityNameExpression(expr); } + function isLateBindableAST(node: DeclarationName) { + if (isComputedPropertyName(node)) { + return isLateBindableExpression(node.expression); + } + else if (isElementAccessExpression(node)) { + return isLateBindableExpression(node.argumentExpression); + } + return false; + } + function isTypeUsableAsIndexSignature(type: Type): boolean { return isTypeAssignableTo(type, stringNumberSymbolType); } diff --git a/tests/cases/compiler/enumKeysInTypeLiteral.ts b/tests/cases/compiler/enumKeysInTypeLiteral.ts new file mode 100644 index 0000000000000..481108289449b --- /dev/null +++ b/tests/cases/compiler/enumKeysInTypeLiteral.ts @@ -0,0 +1,29 @@ + +enum Type { + Foo = 'foo', + '3x14' = '3x14' +} + +type TypeMap = { + [Type.Foo]: 1; + [Type['3x14']]: 2; +} + +const t: TypeMap = { + 'foo': 1, + '3x14': 2 +}; + +enum Numeric { + Negative = -1, + Zero = 0 +} + +type NumericMap = { + // Valid: Accessing enum member via string literal for the name + [Numeric['Negative']]: number; + [Numeric['Zero']]: number; + // Valid: Parenthesized access + [Numeric[('Negative')]]: number; +} + From 91424d844adb355c2252a6a2309155e04080bc67 Mon Sep 17 00:00:00 2001 From: Alessio Attilio Date: Thu, 15 Jan 2026 17:37:50 +0100 Subject: [PATCH 2/2] fix(checker): Allow element access expressions in computed property names if argument is literal --- src/compiler/utilities.ts | 202 +++++++++--------- .../reference/enumKeysInTypeLiteral.js | 48 +++++ .../reference/enumKeysInTypeLiteral.symbols | 71 ++++++ .../reference/enumKeysInTypeLiteral.types | 124 +++++++++++ .../isolatedDeclarationLazySymbols.errors.txt | 6 +- .../isolatedDeclarationLazySymbols.types | 4 +- 6 files changed, 353 insertions(+), 102 deletions(-) create mode 100644 tests/baselines/reference/enumKeysInTypeLiteral.js create mode 100644 tests/baselines/reference/enumKeysInTypeLiteral.symbols create mode 100644 tests/baselines/reference/enumKeysInTypeLiteral.types diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index ba6b6d253a31c..e8cdab41cf5a2 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -811,13 +811,13 @@ export function moduleResolutionIsEqualTo(oldResolution: ResolvedModuleWithFaile return oldResolution === newResolution || oldResolution.resolvedModule === newResolution.resolvedModule || !!oldResolution.resolvedModule && - !!newResolution.resolvedModule && - oldResolution.resolvedModule.isExternalLibraryImport === newResolution.resolvedModule.isExternalLibraryImport && - oldResolution.resolvedModule.extension === newResolution.resolvedModule.extension && - oldResolution.resolvedModule.resolvedFileName === newResolution.resolvedModule.resolvedFileName && - oldResolution.resolvedModule.originalPath === newResolution.resolvedModule.originalPath && - packageIdIsEqual(oldResolution.resolvedModule.packageId, newResolution.resolvedModule.packageId) && - oldResolution.alternateResult === newResolution.alternateResult; + !!newResolution.resolvedModule && + oldResolution.resolvedModule.isExternalLibraryImport === newResolution.resolvedModule.isExternalLibraryImport && + oldResolution.resolvedModule.extension === newResolution.resolvedModule.extension && + oldResolution.resolvedModule.resolvedFileName === newResolution.resolvedModule.resolvedFileName && + oldResolution.resolvedModule.originalPath === newResolution.resolvedModule.originalPath && + packageIdIsEqual(oldResolution.resolvedModule.packageId, newResolution.resolvedModule.packageId) && + oldResolution.alternateResult === newResolution.alternateResult; } /** @internal */ @@ -846,25 +846,25 @@ export function createModuleNotFoundChain(sourceFile: SourceFile, host: TypeChec ...alternateResultMessage[1], ) : host.typesPackageExists(packageName) - ? chainDiagnosticMessages( + ? chainDiagnosticMessages( /*details*/ undefined, - Diagnostics.If_the_0_package_actually_exposes_this_module_consider_sending_a_pull_request_to_amend_https_Colon_Slash_Slashgithub_com_SlashDefinitelyTyped_SlashDefinitelyTyped_Slashtree_Slashmaster_Slashtypes_Slash_1, - packageName, - mangleScopedPackageName(packageName), - ) - : host.packageBundlesTypes(packageName) - ? chainDiagnosticMessages( + Diagnostics.If_the_0_package_actually_exposes_this_module_consider_sending_a_pull_request_to_amend_https_Colon_Slash_Slashgithub_com_SlashDefinitelyTyped_SlashDefinitelyTyped_Slashtree_Slashmaster_Slashtypes_Slash_1, + packageName, + mangleScopedPackageName(packageName), + ) + : host.packageBundlesTypes(packageName) + ? chainDiagnosticMessages( /*details*/ undefined, - Diagnostics.If_the_0_package_actually_exposes_this_module_try_adding_a_new_declaration_d_ts_file_containing_declare_module_1, - packageName, - moduleReference, - ) - : chainDiagnosticMessages( + Diagnostics.If_the_0_package_actually_exposes_this_module_try_adding_a_new_declaration_d_ts_file_containing_declare_module_1, + packageName, + moduleReference, + ) + : chainDiagnosticMessages( /*details*/ undefined, - Diagnostics.Try_npm_i_save_dev_types_Slash_1_if_it_exists_or_add_a_new_declaration_d_ts_file_containing_declare_module_0, - moduleReference, - mangleScopedPackageName(packageName), - ); + Diagnostics.Try_npm_i_save_dev_types_Slash_1_if_it_exists_or_add_a_new_declaration_d_ts_file_containing_declare_module_0, + moduleReference, + mangleScopedPackageName(packageName), + ); if (result) result.repopulateInfo = () => ({ moduleReference, mode, packageName: packageName === moduleReference ? undefined : packageName }); return result; } @@ -888,15 +888,15 @@ export function createModeMismatchDetails(currentSourceFile: SourceFile): Diagno combinePaths(scope.packageDirectory, "package.json"), ) : targetExt ? - chainDiagnosticMessages( + chainDiagnosticMessages( /*details*/ undefined, - Diagnostics.To_convert_this_file_to_an_ECMAScript_module_change_its_file_extension_to_0_or_create_a_local_package_json_file_with_type_Colon_module, - targetExt, - ) : - chainDiagnosticMessages( + Diagnostics.To_convert_this_file_to_an_ECMAScript_module_change_its_file_extension_to_0_or_create_a_local_package_json_file_with_type_Colon_module, + targetExt, + ) : + chainDiagnosticMessages( /*details*/ undefined, - Diagnostics.To_convert_this_file_to_an_ECMAScript_module_create_a_local_package_json_file_with_type_Colon_module, - ); + Diagnostics.To_convert_this_file_to_an_ECMAScript_module_create_a_local_package_json_file_with_type_Colon_module, + ); result.repopulateInfo = () => true; return result; } @@ -920,10 +920,10 @@ export function typeDirectiveIsEqualTo(oldResolution: ResolvedTypeReferenceDirec return oldResolution === newResolution || oldResolution.resolvedTypeReferenceDirective === newResolution.resolvedTypeReferenceDirective || !!oldResolution.resolvedTypeReferenceDirective && - !!newResolution.resolvedTypeReferenceDirective && - oldResolution.resolvedTypeReferenceDirective.resolvedFileName === newResolution.resolvedTypeReferenceDirective.resolvedFileName && - !!oldResolution.resolvedTypeReferenceDirective.primary === !!newResolution.resolvedTypeReferenceDirective.primary && - oldResolution.resolvedTypeReferenceDirective.originalPath === newResolution.resolvedTypeReferenceDirective.originalPath; + !!newResolution.resolvedTypeReferenceDirective && + oldResolution.resolvedTypeReferenceDirective.resolvedFileName === newResolution.resolvedTypeReferenceDirective.resolvedFileName && + !!oldResolution.resolvedTypeReferenceDirective.primary === !!newResolution.resolvedTypeReferenceDirective.primary && + oldResolution.resolvedTypeReferenceDirective.originalPath === newResolution.resolvedTypeReferenceDirective.originalPath; } /** @internal */ @@ -1189,11 +1189,11 @@ export function isRecognizedTripleSlashComment(text: string, commentPos: number, ) { const textSubStr = text.substring(commentPos, commentEnd); return fullTripleSlashReferencePathRegEx.test(textSubStr) || - fullTripleSlashAMDReferencePathRegEx.test(textSubStr) || - fullTripleSlashAMDModuleRegEx.test(textSubStr) || - fullTripleSlashReferenceTypeReferenceDirectiveRegEx.test(textSubStr) || - fullTripleSlashLibReferenceRegEx.test(textSubStr) || - defaultLibReferenceRegEx.test(textSubStr) ? + fullTripleSlashAMDReferencePathRegEx.test(textSubStr) || + fullTripleSlashAMDModuleRegEx.test(textSubStr) || + fullTripleSlashReferenceTypeReferenceDirectiveRegEx.test(textSubStr) || + fullTripleSlashLibReferenceRegEx.test(textSubStr) || + defaultLibReferenceRegEx.test(textSubStr) ? true : false; } return false; @@ -1943,7 +1943,7 @@ export function getLiteralText(node: LiteralLikeNode, sourceFile: SourceFile | u case SyntaxKind.StringLiteral: { const escapeText = flags & GetLiteralTextFlags.JsxAttributeEscape ? escapeJsxAttributeString : flags & GetLiteralTextFlags.NeverAsciiEscape || (getEmitFlags(node) & EmitFlags.NoAsciiEscaping) ? escapeString : - escapeNonAsciiString; + escapeNonAsciiString; if ((node as StringLiteral).singleQuote) { return "'" + escapeText(node.text, CharacterCodes.singleQuote) + "'"; } @@ -2310,7 +2310,9 @@ export function getNameFromIndexInfo(info: IndexInfo): string | undefined { /** @internal */ export function isComputedNonLiteralName(name: PropertyName): boolean { - return name.kind === SyntaxKind.ComputedPropertyName && !isStringOrNumericLiteralLike(name.expression); + return name.kind === SyntaxKind.ComputedPropertyName && + !isStringOrNumericLiteralLike(name.expression) && + !isSignedNumericLiteral(name.expression); } /** @internal */ @@ -2326,6 +2328,12 @@ export function tryGetTextOfPropertyName(name: PropertyName | NoSubstitutionTemp return escapeLeadingUnderscores(name.text); case SyntaxKind.ComputedPropertyName: if (isStringOrNumericLiteralLike(name.expression)) return escapeLeadingUnderscores(name.expression.text); + if (isSignedNumericLiteral(name.expression)) { + if (name.expression.operator === SyntaxKind.MinusToken) { + return tokenToString(name.expression.operator) + name.expression.operand.text as __String; + } + return name.expression.operand.text as __String; + } return undefined; case SyntaxKind.JsxNamespacedName: return getEscapedTextOfJsxNamespacedName(name); @@ -2715,12 +2723,12 @@ export function getLeadingCommentRangesOfNode(node: Node, sourceFileOfNode: Sour /** @internal */ export function getJSDocCommentRanges(node: Node, text: string): CommentRange[] | undefined { const commentRanges = (node.kind === SyntaxKind.Parameter || - node.kind === SyntaxKind.TypeParameter || - node.kind === SyntaxKind.FunctionExpression || - node.kind === SyntaxKind.ArrowFunction || - node.kind === SyntaxKind.ParenthesizedExpression || - node.kind === SyntaxKind.VariableDeclaration || - node.kind === SyntaxKind.ExportSpecifier) ? + node.kind === SyntaxKind.TypeParameter || + node.kind === SyntaxKind.FunctionExpression || + node.kind === SyntaxKind.ArrowFunction || + node.kind === SyntaxKind.ParenthesizedExpression || + node.kind === SyntaxKind.VariableDeclaration || + node.kind === SyntaxKind.ExportSpecifier) ? concatenate(getTrailingCommentRanges(text, node.pos), getLeadingCommentRanges(text, node.pos)) : getLeadingCommentRanges(text, node.pos); // True if the comment starts with '/**' but not if it is '/**/' @@ -2775,7 +2783,7 @@ export function isPartOfTypeNode(node: Node): boolean { } // At this point, node is either a qualified name or an identifier Debug.assert(node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.QualifiedName || node.kind === SyntaxKind.PropertyAccessExpression, "'node' was expected to be a qualified name, identifier or property access in 'isPartOfTypeNode'."); - // falls through + // falls through case SyntaxKind.QualifiedName: case SyntaxKind.PropertyAccessExpression: @@ -2986,7 +2994,7 @@ export function isCommonJsExportPropertyAssignment(node: Node): boolean { export function isValidESSymbolDeclaration(node: Node): boolean { return (isVariableDeclaration(node) ? isVarConst(node) && isIdentifier(node.name) && isVariableDeclarationInVariableStatement(node) : isPropertyDeclaration(node) ? hasEffectiveReadonlyModifier(node) && hasStaticModifier(node) : - isPropertySignature(node) && hasEffectiveReadonlyModifier(node)) || isCommonJsExportPropertyAssignment(node); + isPropertySignature(node) && hasEffectiveReadonlyModifier(node)) || isCommonJsExportPropertyAssignment(node); } /** @internal */ @@ -3179,7 +3187,7 @@ export function getThisContainer(node: Node, includeArrowFunctions: boolean, inc if (!includeArrowFunctions) { continue; } - // falls through + // falls through case SyntaxKind.FunctionDeclaration: case SyntaxKind.FunctionExpression: @@ -3304,7 +3312,7 @@ export function getSuperContainer(node: Node, stopOnFunctions: boolean) { if (!stopOnFunctions) { continue; } - // falls through + // falls through case SyntaxKind.PropertyDeclaration: case SyntaxKind.PropertySignature: @@ -3532,7 +3540,7 @@ export function classElementOrClassElementParameterIsDecorated(useLegacyDecorato const { firstAccessor, secondAccessor, setAccessor } = getAllAccessorDeclarations(parent.members, node); const firstAccessorWithDecorators = hasDecorators(firstAccessor) ? firstAccessor : secondAccessor && hasDecorators(secondAccessor) ? secondAccessor : - undefined; + undefined; if (!firstAccessorWithDecorators || node !== firstAccessorWithDecorators) { return false; } @@ -3640,7 +3648,7 @@ export function isExpressionNode(node: Node): boolean { if (node.parent.kind === SyntaxKind.TypeQuery || isJSDocLinkLike(node.parent) || isJSDocNameReference(node.parent) || isJSDocMemberName(node.parent) || isJSXTagName(node)) { return true; } - // falls through + // falls through case SyntaxKind.NumericLiteral: case SyntaxKind.BigIntLiteral: @@ -3982,7 +3990,7 @@ function getDefaultedExpandoInitializer(name: Expression, initializer: Expressio export function isDefaultedExpandoInitializer(node: BinaryExpression): boolean | undefined { const name = isVariableDeclaration(node.parent) ? node.parent.name : isBinaryExpression(node.parent) && node.parent.operatorToken.kind === SyntaxKind.EqualsToken ? node.parent.left : - undefined; + undefined; return name && getExpandoInitializer(node.right, isPrototypeAccess(name)) && isEntityNameExpression(name) && isSameEntityName(name, node.left); } @@ -4022,9 +4030,9 @@ export function isSameEntityName(name: Expression, initializer: Expression): boo isMemberName(name) && isLiteralLikeAccess(initializer) && (initializer.expression.kind === SyntaxKind.ThisKeyword || isIdentifier(initializer.expression) && - (initializer.expression.escapedText === "window" || - initializer.expression.escapedText === "self" || - initializer.expression.escapedText === "global")) + (initializer.expression.escapedText === "window" || + initializer.expression.escapedText === "self" || + initializer.expression.escapedText === "global")) ) { return isSameEntityName(name, getNameOrArgument(initializer)); } @@ -4262,7 +4270,7 @@ export function setValueDeclaration(symbol: Symbol, node: Declaration): void { if ( !valueDeclaration || !(node.flags & NodeFlags.Ambient && !isInJSFile(node) && !(valueDeclaration.flags & NodeFlags.Ambient)) && - (isAssignmentDeclaration(valueDeclaration) && !isAssignmentDeclaration(node)) || + (isAssignmentDeclaration(valueDeclaration) && !isAssignmentDeclaration(node)) || (valueDeclaration.kind !== node.kind && isEffectiveModuleDeclaration(valueDeclaration)) ) { // other kinds of value declarations take precedence over modules and assignment declarations @@ -4443,18 +4451,18 @@ export function isTypeAlias(node: Node): node is JSDocTypedefTag | JSDocCallback function getSourceOfAssignment(node: Node): Node | undefined { return isExpressionStatement(node) && - isBinaryExpression(node.expression) && - node.expression.operatorToken.kind === SyntaxKind.EqualsToken + isBinaryExpression(node.expression) && + node.expression.operatorToken.kind === SyntaxKind.EqualsToken ? getRightMostAssignedExpression(node.expression) : undefined; } function getSourceOfDefaultedAssignment(node: Node): Node | undefined { return isExpressionStatement(node) && - isBinaryExpression(node.expression) && - getAssignmentDeclarationKind(node.expression) !== AssignmentDeclarationKind.None && - isBinaryExpression(node.expression.right) && - (node.expression.right.operatorToken.kind === SyntaxKind.BarBarToken || node.expression.right.operatorToken.kind === SyntaxKind.QuestionQuestionToken) + isBinaryExpression(node.expression) && + getAssignmentDeclarationKind(node.expression) !== AssignmentDeclarationKind.None && + isBinaryExpression(node.expression.right) && + (node.expression.right.operatorToken.kind === SyntaxKind.BarBarToken || node.expression.right.operatorToken.kind === SyntaxKind.QuestionQuestionToken) ? node.expression.right.right : undefined; } @@ -4478,8 +4486,8 @@ export function getSingleVariableOfVariableStatement(node: Node): VariableDeclar function getNestedModuleDeclaration(node: Node): Node | undefined { return isModuleDeclaration(node) && - node.body && - node.body.kind === SyntaxKind.ModuleDeclaration + node.body && + node.body.kind === SyntaxKind.ModuleDeclaration ? node.body : undefined; } @@ -5036,7 +5044,7 @@ export function getDeclarationFromName(name: Node): Declaration | undefined { case SyntaxKind.NoSubstitutionTemplateLiteral: case SyntaxKind.NumericLiteral: if (isComputedPropertyName(parent)) return parent.parent; - // falls through + // falls through case SyntaxKind.Identifier: if (isDeclaration(parent)) { return parent.name === name ? parent : undefined; @@ -5048,9 +5056,9 @@ export function getDeclarationFromName(name: Node): Declaration | undefined { else { const binExp = parent.parent; return isBinaryExpression(binExp) && - getAssignmentDeclarationKind(binExp) !== AssignmentDeclarationKind.None && - ((binExp.left as BindableStaticNameExpression).symbol || binExp.symbol) && - getNameOfDeclaration(binExp) === name + getAssignmentDeclarationKind(binExp) !== AssignmentDeclarationKind.None && + ((binExp.left as BindableStaticNameExpression).symbol || binExp.symbol) && + getNameOfDeclaration(binExp) === name ? binExp : undefined; } @@ -5182,7 +5190,7 @@ export function getEffectiveImplementsTypeNodes(node: ClassLikeDeclaration): und export function getAllSuperTypeNodes(node: Node): readonly TypeNode[] { return isInterfaceDeclaration(node) ? getInterfaceBaseTypeNodes(node) || emptyArray : isClassLike(node) ? concatenate(singleElementArray(getEffectiveBaseTypeNode(node)), getEffectiveImplementsTypeNodes(node)) || emptyArray : - emptyArray; + emptyArray; } /** @internal */ @@ -5281,7 +5289,7 @@ export function getFunctionFlags(node: SignatureDeclaration | undefined): Functi if (node.asteriskToken) { flags |= FunctionFlags.Generator; } - // falls through + // falls through case SyntaxKind.ArrowFunction: if (hasSyntacticModifier(node, ModifierFlags.Async)) { @@ -6160,7 +6168,7 @@ function getReplacement(c: string, offset: number, input: string) { export function escapeString(s: string, quoteChar?: CharacterCodes.doubleQuote | CharacterCodes.singleQuote | CharacterCodes.backtick): string { const escapedCharsRegExp = quoteChar === CharacterCodes.backtick ? backtickQuoteEscapedCharsRegExp : quoteChar === CharacterCodes.singleQuote ? singleQuoteEscapedCharsRegExp : - doubleQuoteEscapedCharsRegExp; + doubleQuoteEscapedCharsRegExp; return s.replace(escapedCharsRegExp, getReplacement); } @@ -6523,8 +6531,8 @@ export function getDeclarationEmitOutputFilePathWorker(fileName: string, options export function getDeclarationEmitExtensionForPath(path: string): Extension.Dts | Extension.Dmts | Extension.Dcts | ".d.json.ts" { return fileExtensionIsOneOf(path, [Extension.Mjs, Extension.Mts]) ? Extension.Dmts : fileExtensionIsOneOf(path, [Extension.Cjs, Extension.Cts]) ? Extension.Dcts : - fileExtensionIsOneOf(path, [Extension.Json]) ? `.d.json.ts` : // Drive-by redefinition of json declaration file output name so if it's ever enabled, it behaves well - Extension.Dts; + fileExtensionIsOneOf(path, [Extension.Json]) ? `.d.json.ts` : // Drive-by redefinition of json declaration file output name so if it's ever enabled, it behaves well + Extension.Dts; } /** @@ -6535,8 +6543,8 @@ export function getDeclarationEmitExtensionForPath(path: string): Extension.Dts export function getPossibleOriginalInputExtensionForExtension(path: string): Extension[] { return fileExtensionIsOneOf(path, [Extension.Dmts, Extension.Mjs, Extension.Mts]) ? [Extension.Mts, Extension.Mjs] : fileExtensionIsOneOf(path, [Extension.Dcts, Extension.Cjs, Extension.Cts]) ? [Extension.Cts, Extension.Cjs] : - fileExtensionIsOneOf(path, [`.d.json.ts`]) ? [Extension.Json] : - [Extension.Tsx, Extension.Ts, Extension.Jsx, Extension.Js]; + fileExtensionIsOneOf(path, [`.d.json.ts`]) ? [Extension.Json] : + [Extension.Tsx, Extension.Ts, Extension.Jsx, Extension.Js]; } /** @internal */ @@ -8007,7 +8015,7 @@ export function getDeclarationModifierFlagsFromSymbol(s: Symbol, isWrite = false const checkFlags = (s as TransientSymbol).links.checkFlags; const accessModifier = checkFlags & CheckFlags.ContainsPrivate ? ModifierFlags.Private : checkFlags & CheckFlags.ContainsPublic ? ModifierFlags.Public : - ModifierFlags.Protected; + ModifierFlags.Protected; const staticModifier = checkFlags & CheckFlags.ContainsStatic ? ModifierFlags.Static : 0; return accessModifier | staticModifier; } @@ -8393,7 +8401,7 @@ export function getLeftmostExpression(node: Expression, stopAtCallExpressions: b if (stopAtCallExpressions) { return node; } - // falls through + // falls through case SyntaxKind.AsExpression: case SyntaxKind.ElementAccessExpression: case SyntaxKind.PropertyAccessExpression: @@ -9333,7 +9341,7 @@ export function compilerOptionsAffectDeclarationPath(newOptions: CompilerOptions export function getCompilerOptionValue(options: CompilerOptions, option: CommandLineOption): unknown { return option.strictFlag ? getStrictOptionValue(options, option.name as StrictOptionName) : option.allowJsFlag ? getAllowJSCompilerOption(options) : - options[option.name]; + options[option.name]; } /** @internal */ @@ -9352,10 +9360,10 @@ export function getJSXImplicitImportBase(compilerOptions: CompilerOptions, file? return undefined; } return compilerOptions.jsx === JsxEmit.ReactJSX || - compilerOptions.jsx === JsxEmit.ReactJSXDev || - compilerOptions.jsxImportSource || - jsxImportSourcePragma || - jsxRuntimePragma?.arguments.factory === "automatic" ? + compilerOptions.jsx === JsxEmit.ReactJSXDev || + compilerOptions.jsxImportSource || + jsxImportSourcePragma || + jsxRuntimePragma?.arguments.factory === "automatic" ? jsxImportSourcePragma?.arguments.factory || compilerOptions.jsxImportSource || "react" : undefined; } @@ -10028,7 +10036,7 @@ export function getModuleSpecifierEndingPreference(preference: UserPreferences[" let usesJsExtensions = false; const specifiers = sourceFile?.imports.length ? sourceFile.imports : sourceFile && isSourceFileJS(sourceFile) ? getRequiresAtTopOfFile(sourceFile).map(r => r.arguments[0]) : - emptyArray; + emptyArray; for (const specifier of specifiers) { if (pathIsRelative(specifier.text)) { if ( @@ -10431,7 +10439,7 @@ export function parsePseudoBigInt(stringValue: string): string { const digit = digitChar <= CharacterCodes._9 ? digitChar - CharacterCodes._0 : 10 + digitChar - - (digitChar <= CharacterCodes.F ? CharacterCodes.A : CharacterCodes.a); + (digitChar <= CharacterCodes.F ? CharacterCodes.A : CharacterCodes.a); const shiftedDigit = digit << (bitOffset & 15); segments[segment] |= shiftedDigit; const residual = shiftedDigit >>> 16; @@ -10786,7 +10794,7 @@ export function getContainingNodeArray(node: Node): NodeArray | undefined case SyntaxKind.NewExpression: return isTypeNode(node) ? (parent as CallExpression | NewExpression).typeArguments : (parent as CallExpression | NewExpression).expression === node ? undefined : - (parent as CallExpression | NewExpression).arguments; + (parent as CallExpression | NewExpression).arguments; case SyntaxKind.JsxElement: case SyntaxKind.JsxFragment: return isJsxChild(node) ? (parent as JsxElement | JsxFragment).children : undefined; @@ -10881,7 +10889,7 @@ export function createPropertyNameNodeForIdentifierOrLiteral(name: string, targe const isMethodNamedNew = isMethod && name === "new"; return !isMethodNamedNew && isIdentifierText(name, target) ? factory.createIdentifier(name) : !stringNamed && !isMethodNamedNew && isNumericLiteralName(name) && +name >= 0 ? factory.createNumericLiteral(+name) : - factory.createStringLiteral(name, !!singleQuote); + factory.createStringLiteral(name, !!singleQuote); } /** @internal */ @@ -11487,11 +11495,11 @@ export function createNameResolver({ useResult = result.flags & SymbolFlags.TypeParameter // type parameters are visible in parameter list, return type and type parameter list ? !!(lastLocation.flags & NodeFlags.Synthesized) || // Synthetic fake scopes are added for signatures so type parameters are accessible from them - lastLocation === (location as FunctionLikeDeclaration).type || - lastLocation.kind === SyntaxKind.Parameter || - lastLocation.kind === SyntaxKind.JSDocParameterTag || - lastLocation.kind === SyntaxKind.JSDocReturnTag || - lastLocation.kind === SyntaxKind.TypeParameter + lastLocation === (location as FunctionLikeDeclaration).type || + lastLocation.kind === SyntaxKind.Parameter || + lastLocation.kind === SyntaxKind.JSDocParameterTag || + lastLocation.kind === SyntaxKind.JSDocReturnTag || + lastLocation.kind === SyntaxKind.TypeParameter // local types not visible outside the function body : false; } @@ -11532,7 +11540,7 @@ export function createNameResolver({ switch (location.kind) { case SyntaxKind.SourceFile: if (!isExternalOrCommonJsModule(location as SourceFile)) break; - // falls through + // falls through case SyntaxKind.ModuleDeclaration: const moduleExports = getSymbolOfDeclaration(location as SourceFile | ModuleDeclaration)?.exports || emptySymbols; if (location.kind === SyntaxKind.SourceFile || (isModuleDeclaration(location) && location.flags & NodeFlags.Ambient && !isGlobalScopeAugmentation(location))) { @@ -11678,7 +11686,7 @@ export function createNameResolver({ if (getEmitScriptTarget(compilerOptions) >= ScriptTarget.ES2015) { break; } - // falls through + // falls through case SyntaxKind.MethodDeclaration: case SyntaxKind.Constructor: case SyntaxKind.GetAccessor: @@ -11792,7 +11800,7 @@ export function createNameResolver({ lastLocation = location; location = isJSDocTemplateTag(location) ? getEffectiveContainerForJSDocTemplateTag(location) || location.parent : isJSDocParameterTag(location) || isJSDocReturnTag(location) ? getHostSignatureFromJSDoc(location) || location.parent : - location.parent; + location.parent; } // We just climbed up parents looking for the name, meaning that we started in a descendant node of `lastLocation`. @@ -12289,7 +12297,7 @@ function getSynthesizedDeepCloneWorker(node: T, replaceNode?: (n // This only happens for leaf nodes - internal nodes always see their children change. const clone = isStringLiteral(node) ? setOriginalNode(factory.createStringLiteralFromNode(node), node) as Node as T : isNumericLiteral(node) ? setOriginalNode(factory.createNumericLiteral(node.text, node.numericLiteralFlags), node) as Node as T : - factory.cloneNode(node); + factory.cloneNode(node); return setTextRange(clone, node); } diff --git a/tests/baselines/reference/enumKeysInTypeLiteral.js b/tests/baselines/reference/enumKeysInTypeLiteral.js new file mode 100644 index 0000000000000..5ced5711974d4 --- /dev/null +++ b/tests/baselines/reference/enumKeysInTypeLiteral.js @@ -0,0 +1,48 @@ +//// [tests/cases/compiler/enumKeysInTypeLiteral.ts] //// + +//// [enumKeysInTypeLiteral.ts] +enum Type { + Foo = 'foo', + '3x14' = '3x14' +} + +type TypeMap = { + [Type.Foo]: 1; + [Type['3x14']]: 2; +} + +const t: TypeMap = { + 'foo': 1, + '3x14': 2 +}; + +enum Numeric { + Negative = -1, + Zero = 0 +} + +type NumericMap = { + // Valid: Accessing enum member via string literal for the name + [Numeric['Negative']]: number; + [Numeric['Zero']]: number; + // Valid: Parenthesized access + [Numeric[('Negative')]]: number; +} + + + +//// [enumKeysInTypeLiteral.js] +var Type; +(function (Type) { + Type["Foo"] = "foo"; + Type["3x14"] = "3x14"; +})(Type || (Type = {})); +var t = { + 'foo': 1, + '3x14': 2 +}; +var Numeric; +(function (Numeric) { + Numeric[Numeric["Negative"] = -1] = "Negative"; + Numeric[Numeric["Zero"] = 0] = "Zero"; +})(Numeric || (Numeric = {})); diff --git a/tests/baselines/reference/enumKeysInTypeLiteral.symbols b/tests/baselines/reference/enumKeysInTypeLiteral.symbols new file mode 100644 index 0000000000000..7585e72aea178 --- /dev/null +++ b/tests/baselines/reference/enumKeysInTypeLiteral.symbols @@ -0,0 +1,71 @@ +//// [tests/cases/compiler/enumKeysInTypeLiteral.ts] //// + +=== enumKeysInTypeLiteral.ts === +enum Type { +>Type : Symbol(Type, Decl(enumKeysInTypeLiteral.ts, 0, 0)) + + Foo = 'foo', +>Foo : Symbol(Type.Foo, Decl(enumKeysInTypeLiteral.ts, 0, 11)) + + '3x14' = '3x14' +>'3x14' : Symbol(Type['3x14'], Decl(enumKeysInTypeLiteral.ts, 1, 14)) +} + +type TypeMap = { +>TypeMap : Symbol(TypeMap, Decl(enumKeysInTypeLiteral.ts, 3, 1)) + + [Type.Foo]: 1; +>[Type.Foo] : Symbol([Type.Foo], Decl(enumKeysInTypeLiteral.ts, 5, 16)) +>Type.Foo : Symbol(Type.Foo, Decl(enumKeysInTypeLiteral.ts, 0, 11)) +>Type : Symbol(Type, Decl(enumKeysInTypeLiteral.ts, 0, 0)) +>Foo : Symbol(Type.Foo, Decl(enumKeysInTypeLiteral.ts, 0, 11)) + + [Type['3x14']]: 2; +>[Type['3x14']] : Symbol([Type['3x14']], Decl(enumKeysInTypeLiteral.ts, 6, 16)) +>Type : Symbol(Type, Decl(enumKeysInTypeLiteral.ts, 0, 0)) +>'3x14' : Symbol(Type['3x14'], Decl(enumKeysInTypeLiteral.ts, 1, 14)) +} + +const t: TypeMap = { +>t : Symbol(t, Decl(enumKeysInTypeLiteral.ts, 10, 5)) +>TypeMap : Symbol(TypeMap, Decl(enumKeysInTypeLiteral.ts, 3, 1)) + + 'foo': 1, +>'foo' : Symbol('foo', Decl(enumKeysInTypeLiteral.ts, 10, 20)) + + '3x14': 2 +>'3x14' : Symbol('3x14', Decl(enumKeysInTypeLiteral.ts, 11, 13)) + +}; + +enum Numeric { +>Numeric : Symbol(Numeric, Decl(enumKeysInTypeLiteral.ts, 13, 2)) + + Negative = -1, +>Negative : Symbol(Numeric.Negative, Decl(enumKeysInTypeLiteral.ts, 15, 14)) + + Zero = 0 +>Zero : Symbol(Numeric.Zero, Decl(enumKeysInTypeLiteral.ts, 16, 18)) +} + +type NumericMap = { +>NumericMap : Symbol(NumericMap, Decl(enumKeysInTypeLiteral.ts, 18, 1)) + + // Valid: Accessing enum member via string literal for the name + [Numeric['Negative']]: number; +>[Numeric['Negative']] : Symbol([Numeric['Negative']], Decl(enumKeysInTypeLiteral.ts, 20, 19), Decl(enumKeysInTypeLiteral.ts, 23, 30)) +>Numeric : Symbol(Numeric, Decl(enumKeysInTypeLiteral.ts, 13, 2)) +>'Negative' : Symbol(Numeric.Negative, Decl(enumKeysInTypeLiteral.ts, 15, 14)) + + [Numeric['Zero']]: number; +>[Numeric['Zero']] : Symbol([Numeric['Zero']], Decl(enumKeysInTypeLiteral.ts, 22, 34)) +>Numeric : Symbol(Numeric, Decl(enumKeysInTypeLiteral.ts, 13, 2)) +>'Zero' : Symbol(Numeric.Zero, Decl(enumKeysInTypeLiteral.ts, 16, 18)) + + // Valid: Parenthesized access + [Numeric[('Negative')]]: number; +>[Numeric[('Negative')]] : Symbol([Numeric['Negative']], Decl(enumKeysInTypeLiteral.ts, 20, 19), Decl(enumKeysInTypeLiteral.ts, 23, 30)) +>Numeric : Symbol(Numeric, Decl(enumKeysInTypeLiteral.ts, 13, 2)) +} + + diff --git a/tests/baselines/reference/enumKeysInTypeLiteral.types b/tests/baselines/reference/enumKeysInTypeLiteral.types new file mode 100644 index 0000000000000..4dee1182693b4 --- /dev/null +++ b/tests/baselines/reference/enumKeysInTypeLiteral.types @@ -0,0 +1,124 @@ +//// [tests/cases/compiler/enumKeysInTypeLiteral.ts] //// + +=== enumKeysInTypeLiteral.ts === +enum Type { +>Type : Type +> : ^^^^ + + Foo = 'foo', +>Foo : Type.Foo +> : ^^^^^^^^ +>'foo' : "foo" +> : ^^^^^ + + '3x14' = '3x14' +>'3x14' : (typeof Type)["3x14"] +> : ^^^^^^^^^^^^^^^^^^^^^ +>'3x14' : "3x14" +> : ^^^^^^ +} + +type TypeMap = { +>TypeMap : TypeMap +> : ^^^^^^^ + + [Type.Foo]: 1; +>[Type.Foo] : 1 +> : ^ +>Type.Foo : Type.Foo +> : ^^^^^^^^ +>Type : typeof Type +> : ^^^^^^^^^^^ +>Foo : Type.Foo +> : ^^^^^^^^ + + [Type['3x14']]: 2; +>[Type['3x14']] : 2 +> : ^ +>Type['3x14'] : (typeof Type)["3x14"] +> : ^^^^^^^^^^^^^^^^^^^^^ +>Type : typeof Type +> : ^^^^^^^^^^^ +>'3x14' : "3x14" +> : ^^^^^^ +} + +const t: TypeMap = { +>t : TypeMap +> : ^^^^^^^ +>{ 'foo': 1, '3x14': 2} : { foo: 1; '3x14': 2; } +> : ^^^^^^^^^^^^^^^^^^^^^^ + + 'foo': 1, +>'foo' : 1 +> : ^ +>1 : 1 +> : ^ + + '3x14': 2 +>'3x14' : 2 +> : ^ +>2 : 2 +> : ^ + +}; + +enum Numeric { +>Numeric : Numeric +> : ^^^^^^^ + + Negative = -1, +>Negative : Numeric.Negative +> : ^^^^^^^^^^^^^^^^ +>-1 : -1 +> : ^^ +>1 : 1 +> : ^ + + Zero = 0 +>Zero : Numeric.Zero +> : ^^^^^^^^^^^^ +>0 : 0 +> : ^ +} + +type NumericMap = { +>NumericMap : NumericMap +> : ^^^^^^^^^^ + + // Valid: Accessing enum member via string literal for the name + [Numeric['Negative']]: number; +>[Numeric['Negative']] : number +> : ^^^^^^ +>Numeric['Negative'] : Numeric.Negative +> : ^^^^^^^^^^^^^^^^ +>Numeric : typeof Numeric +> : ^^^^^^^^^^^^^^ +>'Negative' : "Negative" +> : ^^^^^^^^^^ + + [Numeric['Zero']]: number; +>[Numeric['Zero']] : number +> : ^^^^^^ +>Numeric['Zero'] : Numeric.Zero +> : ^^^^^^^^^^^^ +>Numeric : typeof Numeric +> : ^^^^^^^^^^^^^^ +>'Zero' : "Zero" +> : ^^^^^^ + + // Valid: Parenthesized access + [Numeric[('Negative')]]: number; +>[Numeric[('Negative')]] : number +> : ^^^^^^ +>Numeric[('Negative')] : Numeric.Negative +> : ^^^^^^^^^^^^^^^^ +>Numeric : typeof Numeric +> : ^^^^^^^^^^^^^^ +>('Negative') : "Negative" +> : ^^^^^^^^^^ +>'Negative' : "Negative" +> : ^^^^^^^^^^ +} + + 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"