From c51645a46981bb0bbd3e66cdf753a02a1a3b63a0 Mon Sep 17 00:00:00 2001 From: Jan Kuehle Date: Mon, 11 Dec 2023 13:41:35 +0000 Subject: [PATCH 01/10] isolatedModules errors for non-literal enum initializers --- src/compiler/checker.ts | 64 +++++++++++++++++-- src/compiler/diagnosticMessages.json | 8 +++ ...zerFollowsNonLiteralInitializer.errors.txt | 34 ++++++++++ ...WithNonLiteralStringInitializer.errors.txt | 22 +++++++ ...ModulesGlobalNamespacesAndEnums.errors.txt | 5 +- ...InitializerFollowsNonLiteralInitializer.ts | 32 ++++++++++ .../enumWithNonLiteralStringInitializer.ts | 20 ++++++ 7 files changed, 178 insertions(+), 7 deletions(-) create mode 100644 tests/baselines/reference/enumNoInitializerFollowsNonLiteralInitializer.errors.txt create mode 100644 tests/baselines/reference/enumWithNonLiteralStringInitializer.errors.txt create mode 100644 tests/cases/compiler/enumNoInitializerFollowsNonLiteralInitializer.ts create mode 100644 tests/cases/compiler/enumWithNonLiteralStringInitializer.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b0ea7ea9ffb04..be8469f5d671b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -45010,15 +45010,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (!(nodeLinks.flags & NodeCheckFlags.EnumValuesComputed)) { nodeLinks.flags |= NodeCheckFlags.EnumValuesComputed; let autoValue: number | undefined = 0; + let previous: EnumMember | undefined; for (const member of node.members) { - const value = computeMemberValue(member, autoValue); + const value = computeMemberValue(member, autoValue, previous); getNodeLinks(member).enumMemberValue = value; autoValue = typeof value === "number" ? value + 1 : undefined; + previous = member; } } } - function computeMemberValue(member: EnumMember, autoValue: number | undefined) { + function computeMemberValue(member: EnumMember, autoValue: number | undefined, previous: EnumMember | undefined) { if (isComputedNonLiteralName(member.name)) { error(member.name, Diagnostics.Computed_property_names_are_not_allowed_in_enums); } @@ -45040,11 +45042,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // If the member is the first member in the enum declaration, it is assigned the value zero. // Otherwise, it is assigned the value of the immediately preceding member plus one, and an error // occurs if the immediately preceding member is not a constant enum member. - if (autoValue !== undefined) { - return autoValue; + if (autoValue === undefined) { + error(member.name, Diagnostics.Enum_member_must_have_initializer); + return undefined; } - error(member.name, Diagnostics.Enum_member_must_have_initializer); - return undefined; + if (compilerOptions.isolatedModules && previous?.initializer && !evaluatesToNumericLiteral(previous.initializer)) { + error( + member.name, + Diagnostics.Enum_member_following_a_non_literal_numeric_member_must_have_an_initializer_when_isolatedModules_is_enabled, + ); + } + return autoValue; } function computeConstantValue(member: EnumMember): string | number | undefined { @@ -45060,6 +45068,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { Diagnostics.const_enum_member_initializer_was_evaluated_to_a_non_finite_value, ); } + else if (compilerOptions.isolatedModules && typeof value === "string" && !evaluatesToStringLiteral(initializer)) { + error( + initializer, + Diagnostics.A_member_initializer_in_a_enum_declaration_for_a_string_value_must_be_a_string_literal_when_isolatedModules_is_enabled, + ); + } } else if (isConstEnum) { error(initializer, Diagnostics.const_enum_member_initializers_must_be_constant_expressions); @@ -45073,6 +45087,44 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return value; } + function evaluatesToNumericLiteral(expr: Expression): boolean { + switch (expr.kind) { + case SyntaxKind.PrefixUnaryExpression: + return evaluatesToNumericLiteral((expr as PrefixUnaryExpression).operand); + case SyntaxKind.BinaryExpression: + return evaluatesToNumericLiteral((expr as BinaryExpression).left) && evaluatesToNumericLiteral((expr as BinaryExpression).right); + case SyntaxKind.ParenthesizedExpression: + return evaluatesToNumericLiteral((expr as ParenthesizedExpression).expression); + case SyntaxKind.NumericLiteral: + return true; + } + return false; + } + + function evaluatesToStringLiteral(expr: Expression): boolean { + switch (expr.kind) { + case SyntaxKind.BinaryExpression: + const left = (expr as BinaryExpression).left; + const right = (expr as BinaryExpression).right; + const leftIsNumeric = evaluatesToNumericLiteral(left); + const rightIsNumeric = evaluatesToNumericLiteral(right); + return ( + !(leftIsNumeric && rightIsNumeric) && + (evaluatesToStringLiteral(left) || leftIsNumeric) && + (evaluatesToStringLiteral(right) || rightIsNumeric) && + (expr as BinaryExpression).operatorToken.kind === SyntaxKind.PlusToken + ); + case SyntaxKind.TemplateExpression: + return (expr as TemplateExpression).templateSpans.every(span => evaluatesToStringLiteral(span.expression)); + case SyntaxKind.ParenthesizedExpression: + return evaluatesToStringLiteral((expr as ParenthesizedExpression).expression); + case SyntaxKind.StringLiteral: + case SyntaxKind.NoSubstitutionTemplateLiteral: + return true; + } + return false; + } + function evaluate(expr: Expression, location?: Declaration): string | number | undefined { switch (expr.kind) { case SyntaxKind.PrefixUnaryExpression: diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 80c55bd7ef277..46e2b0106b1d2 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -7936,5 +7936,13 @@ "'await using' statements cannot be used inside a class static block.": { "category": "Error", "code": 18054 + }, + "A member initializer in a enum declaration for a string value must be a string literal when 'isolatedModules' is enabled.": { + "category": "Error", + "code": 18055 + }, + "Enum member following a non-literal numeric member must have an initializer when 'isolatedModules' is enabled.": { + "category": "Error", + "code": 18056 } } diff --git a/tests/baselines/reference/enumNoInitializerFollowsNonLiteralInitializer.errors.txt b/tests/baselines/reference/enumNoInitializerFollowsNonLiteralInitializer.errors.txt new file mode 100644 index 0000000000000..2999b769c9699 --- /dev/null +++ b/tests/baselines/reference/enumNoInitializerFollowsNonLiteralInitializer.errors.txt @@ -0,0 +1,34 @@ +bad.ts(4,5): error TS18056: Enum member following a non-literal numeric member must have an initializer when 'isolatedModules' is enabled. + + +==== ./helpers.ts (0 errors) ==== + export const foo = 2; + +==== ./bad.ts (1 errors) ==== + import { foo } from "./helpers"; + enum A { + a = foo, + b, + ~ +!!! error TS18056: Enum member following a non-literal numeric member must have an initializer when 'isolatedModules' is enabled. + } + +==== ./good.ts (0 errors) ==== + import { foo } from "./helpers"; + enum A { + a = foo, + b = 3, + } + enum B { + a = 1 + 1, + b, + } + enum C { + a = +2, + b, + } + enum D { + a = (2), + b, + } + \ No newline at end of file diff --git a/tests/baselines/reference/enumWithNonLiteralStringInitializer.errors.txt b/tests/baselines/reference/enumWithNonLiteralStringInitializer.errors.txt new file mode 100644 index 0000000000000..f54e34a33d21e --- /dev/null +++ b/tests/baselines/reference/enumWithNonLiteralStringInitializer.errors.txt @@ -0,0 +1,22 @@ +bad.ts(3,8): error TS18055: A member initializer in a enum declaration for a string value must be a string literal when 'isolatedModules' is enabled. + + +==== ./helpers.ts (0 errors) ==== + export const foo = 2; + +==== ./bad.ts (1 errors) ==== + import { foo } from "./helpers"; + enum A { + a = `${foo}` + ~~~~~~~~ +!!! error TS18055: A member initializer in a enum declaration for a string value must be a string literal when 'isolatedModules' is enabled. + } + +==== ./good.ts (0 errors) ==== + enum A { + a = `${"foo"}`, + b = "" + 2, + c = 2 + "", + d = ("foo"), + } + \ No newline at end of file diff --git a/tests/baselines/reference/isolatedModulesGlobalNamespacesAndEnums.errors.txt b/tests/baselines/reference/isolatedModulesGlobalNamespacesAndEnums.errors.txt index c0c853cca68e6..ca2f944b3b485 100644 --- a/tests/baselines/reference/isolatedModulesGlobalNamespacesAndEnums.errors.txt +++ b/tests/baselines/reference/isolatedModulesGlobalNamespacesAndEnums.errors.txt @@ -1,3 +1,4 @@ +enum2.ts(2,9): error TS18055: A member initializer in a enum declaration for a string value must be a string literal when 'isolatedModules' is enabled. enum2.ts(3,9): error TS1281: Cannot access 'A' from another file without qualification when 'isolatedModules' is enabled. Use 'Enum.A' instead. enum2.ts(4,9): error TS1281: Cannot access 'X' from another file without qualification when 'isolatedModules' is enabled. Use 'Enum.X' instead. script-namespaces.ts(1,11): error TS1280: Namespaces are not allowed in global script files when 'isolatedModules' is enabled. If this file is not intended to be a global script, set 'moduleDetection' to 'force' or add an empty 'export {}' statement. @@ -26,9 +27,11 @@ script-namespaces.ts(1,11): error TS1280: Namespaces are not allowed in global s declare enum Enum { X = 1_000_000 } const d = 'd'; -==== enum2.ts (2 errors) ==== +==== enum2.ts (3 errors) ==== enum Enum { D = d, + ~ +!!! error TS18055: A member initializer in a enum declaration for a string value must be a string literal when 'isolatedModules' is enabled. E = A, // error ~ !!! error TS1281: Cannot access 'A' from another file without qualification when 'isolatedModules' is enabled. Use 'Enum.A' instead. diff --git a/tests/cases/compiler/enumNoInitializerFollowsNonLiteralInitializer.ts b/tests/cases/compiler/enumNoInitializerFollowsNonLiteralInitializer.ts new file mode 100644 index 0000000000000..7f64a8b2eea1c --- /dev/null +++ b/tests/cases/compiler/enumNoInitializerFollowsNonLiteralInitializer.ts @@ -0,0 +1,32 @@ +// @isolatedModules: true +// @noEmit: true +// @noTypesAndSymbols: true + +// @filename: ./helpers.ts +export const foo = 2; + +// @filename: ./bad.ts +import { foo } from "./helpers"; +enum A { + a = foo, + b, +} + +// @filename: ./good.ts +import { foo } from "./helpers"; +enum A { + a = foo, + b = 3, +} +enum B { + a = 1 + 1, + b, +} +enum C { + a = +2, + b, +} +enum D { + a = (2), + b, +} diff --git a/tests/cases/compiler/enumWithNonLiteralStringInitializer.ts b/tests/cases/compiler/enumWithNonLiteralStringInitializer.ts new file mode 100644 index 0000000000000..ee6002638cab3 --- /dev/null +++ b/tests/cases/compiler/enumWithNonLiteralStringInitializer.ts @@ -0,0 +1,20 @@ +// @isolatedModules: true +// @noEmit: true +// @noTypesAndSymbols: true + +// @filename: ./helpers.ts +export const foo = 2; + +// @filename: ./bad.ts +import { foo } from "./helpers"; +enum A { + a = `${foo}` +} + +// @filename: ./good.ts +enum A { + a = `${"foo"}`, + b = "" + 2, + c = 2 + "", + d = ("foo"), +} From 93b0b891cfad051698701ca91428f7c02d641466 Mon Sep 17 00:00:00 2001 From: Jan Kuehle Date: Mon, 8 Jan 2024 14:53:57 +0000 Subject: [PATCH 02/10] use getIsolatedModules() & isSyntacticallyNumeric/String --- src/compiler/checker.ts | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index be8469f5d671b..2d25f0ad3999f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -45046,7 +45046,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { error(member.name, Diagnostics.Enum_member_must_have_initializer); return undefined; } - if (compilerOptions.isolatedModules && previous?.initializer && !evaluatesToNumericLiteral(previous.initializer)) { + if (getIsolatedModules(compilerOptions) && previous?.initializer && !isSyntacticallyNumeric(previous.initializer)) { error( member.name, Diagnostics.Enum_member_following_a_non_literal_numeric_member_must_have_an_initializer_when_isolatedModules_is_enabled, @@ -45068,7 +45068,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { Diagnostics.const_enum_member_initializer_was_evaluated_to_a_non_finite_value, ); } - else if (compilerOptions.isolatedModules && typeof value === "string" && !evaluatesToStringLiteral(initializer)) { + else if (getIsolatedModules(compilerOptions) && typeof value === "string" && !isSyntacticallyString(initializer)) { error( initializer, Diagnostics.A_member_initializer_in_a_enum_declaration_for_a_string_value_must_be_a_string_literal_when_isolatedModules_is_enabled, @@ -45087,37 +45087,37 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return value; } - function evaluatesToNumericLiteral(expr: Expression): boolean { + function isSyntacticallyNumeric(expr: Expression): boolean { switch (expr.kind) { case SyntaxKind.PrefixUnaryExpression: - return evaluatesToNumericLiteral((expr as PrefixUnaryExpression).operand); + return isSyntacticallyNumeric((expr as PrefixUnaryExpression).operand); case SyntaxKind.BinaryExpression: - return evaluatesToNumericLiteral((expr as BinaryExpression).left) && evaluatesToNumericLiteral((expr as BinaryExpression).right); + return isSyntacticallyNumeric((expr as BinaryExpression).left) && isSyntacticallyNumeric((expr as BinaryExpression).right); case SyntaxKind.ParenthesizedExpression: - return evaluatesToNumericLiteral((expr as ParenthesizedExpression).expression); + return isSyntacticallyNumeric((expr as ParenthesizedExpression).expression); case SyntaxKind.NumericLiteral: return true; } return false; } - function evaluatesToStringLiteral(expr: Expression): boolean { + function isSyntacticallyString(expr: Expression): boolean { switch (expr.kind) { case SyntaxKind.BinaryExpression: const left = (expr as BinaryExpression).left; const right = (expr as BinaryExpression).right; - const leftIsNumeric = evaluatesToNumericLiteral(left); - const rightIsNumeric = evaluatesToNumericLiteral(right); + const leftIsNumeric = isSyntacticallyNumeric(left); + const rightIsNumeric = isSyntacticallyNumeric(right); return ( !(leftIsNumeric && rightIsNumeric) && - (evaluatesToStringLiteral(left) || leftIsNumeric) && - (evaluatesToStringLiteral(right) || rightIsNumeric) && + (isSyntacticallyString(left) || leftIsNumeric) && + (isSyntacticallyString(right) || rightIsNumeric) && (expr as BinaryExpression).operatorToken.kind === SyntaxKind.PlusToken ); case SyntaxKind.TemplateExpression: - return (expr as TemplateExpression).templateSpans.every(span => evaluatesToStringLiteral(span.expression)); + return (expr as TemplateExpression).templateSpans.every(span => isSyntacticallyString(span.expression)); case SyntaxKind.ParenthesizedExpression: - return evaluatesToStringLiteral((expr as ParenthesizedExpression).expression); + return isSyntacticallyString((expr as ParenthesizedExpression).expression); case SyntaxKind.StringLiteral: case SyntaxKind.NoSubstitutionTemplateLiteral: return true; From af97344ee492c7b6231d03d649d780f50f44467f Mon Sep 17 00:00:00 2001 From: Jan Kuehle Date: Wed, 10 Jan 2024 10:31:59 +0000 Subject: [PATCH 03/10] update error message --- src/compiler/checker.ts | 2 +- src/compiler/diagnosticMessages.json | 2 +- .../reference/enumWithNonLiteralStringInitializer.errors.txt | 4 ++-- .../isolatedModulesGlobalNamespacesAndEnums.errors.txt | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2d25f0ad3999f..5bd23b5d5d3e2 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -45071,7 +45071,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { else if (getIsolatedModules(compilerOptions) && typeof value === "string" && !isSyntacticallyString(initializer)) { error( initializer, - Diagnostics.A_member_initializer_in_a_enum_declaration_for_a_string_value_must_be_a_string_literal_when_isolatedModules_is_enabled, + Diagnostics.A_string_member_initializer_in_a_enum_declaration_can_only_use_constant_expressions_when_isolatedModules_is_enabled, ); } } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 46e2b0106b1d2..f7caafba71918 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -7937,7 +7937,7 @@ "category": "Error", "code": 18054 }, - "A member initializer in a enum declaration for a string value must be a string literal when 'isolatedModules' is enabled.": { + "A string member initializer in a enum declaration can only use constant expressions when 'isolatedModules' is enabled.": { "category": "Error", "code": 18055 }, diff --git a/tests/baselines/reference/enumWithNonLiteralStringInitializer.errors.txt b/tests/baselines/reference/enumWithNonLiteralStringInitializer.errors.txt index f54e34a33d21e..a69a0fde02a65 100644 --- a/tests/baselines/reference/enumWithNonLiteralStringInitializer.errors.txt +++ b/tests/baselines/reference/enumWithNonLiteralStringInitializer.errors.txt @@ -1,4 +1,4 @@ -bad.ts(3,8): error TS18055: A member initializer in a enum declaration for a string value must be a string literal when 'isolatedModules' is enabled. +bad.ts(3,8): error TS18055: A string member initializer in a enum declaration can only use constant expressions when 'isolatedModules' is enabled. ==== ./helpers.ts (0 errors) ==== @@ -9,7 +9,7 @@ bad.ts(3,8): error TS18055: A member initializer in a enum declaration for a str enum A { a = `${foo}` ~~~~~~~~ -!!! error TS18055: A member initializer in a enum declaration for a string value must be a string literal when 'isolatedModules' is enabled. +!!! error TS18055: A string member initializer in a enum declaration can only use constant expressions when 'isolatedModules' is enabled. } ==== ./good.ts (0 errors) ==== diff --git a/tests/baselines/reference/isolatedModulesGlobalNamespacesAndEnums.errors.txt b/tests/baselines/reference/isolatedModulesGlobalNamespacesAndEnums.errors.txt index ca2f944b3b485..c8c456a221157 100644 --- a/tests/baselines/reference/isolatedModulesGlobalNamespacesAndEnums.errors.txt +++ b/tests/baselines/reference/isolatedModulesGlobalNamespacesAndEnums.errors.txt @@ -1,4 +1,4 @@ -enum2.ts(2,9): error TS18055: A member initializer in a enum declaration for a string value must be a string literal when 'isolatedModules' is enabled. +enum2.ts(2,9): error TS18055: A string member initializer in a enum declaration can only use constant expressions when 'isolatedModules' is enabled. enum2.ts(3,9): error TS1281: Cannot access 'A' from another file without qualification when 'isolatedModules' is enabled. Use 'Enum.A' instead. enum2.ts(4,9): error TS1281: Cannot access 'X' from another file without qualification when 'isolatedModules' is enabled. Use 'Enum.X' instead. script-namespaces.ts(1,11): error TS1280: Namespaces are not allowed in global script files when 'isolatedModules' is enabled. If this file is not intended to be a global script, set 'moduleDetection' to 'force' or add an empty 'export {}' statement. @@ -31,7 +31,7 @@ script-namespaces.ts(1,11): error TS1280: Namespaces are not allowed in global s enum Enum { D = d, ~ -!!! error TS18055: A member initializer in a enum declaration for a string value must be a string literal when 'isolatedModules' is enabled. +!!! error TS18055: A string member initializer in a enum declaration can only use constant expressions when 'isolatedModules' is enabled. E = A, // error ~ !!! error TS1281: Cannot access 'A' from another file without qualification when 'isolatedModules' is enabled. Use 'Enum.A' instead. From 6325c1dd631db2d12a03f70bb6f9e23491401551 Mon Sep 17 00:00:00 2001 From: Jan Kuehle Date: Thu, 25 Jan 2024 13:36:57 +0000 Subject: [PATCH 04/10] Remove @noEmit from tests --- ...InitializerFollowsNonLiteralInitializer.js | 70 +++++++++++++++++++ .../enumWithNonLiteralStringInitializer.js | 41 +++++++++++ ...InitializerFollowsNonLiteralInitializer.ts | 1 - .../enumWithNonLiteralStringInitializer.ts | 1 - 4 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/enumNoInitializerFollowsNonLiteralInitializer.js create mode 100644 tests/baselines/reference/enumWithNonLiteralStringInitializer.js diff --git a/tests/baselines/reference/enumNoInitializerFollowsNonLiteralInitializer.js b/tests/baselines/reference/enumNoInitializerFollowsNonLiteralInitializer.js new file mode 100644 index 0000000000000..1b24257e04f7c --- /dev/null +++ b/tests/baselines/reference/enumNoInitializerFollowsNonLiteralInitializer.js @@ -0,0 +1,70 @@ +//// [tests/cases/compiler/enumNoInitializerFollowsNonLiteralInitializer.ts] //// + +//// [helpers.ts] +export const foo = 2; + +//// [bad.ts] +import { foo } from "./helpers"; +enum A { + a = foo, + b, +} + +//// [good.ts] +import { foo } from "./helpers"; +enum A { + a = foo, + b = 3, +} +enum B { + a = 1 + 1, + b, +} +enum C { + a = +2, + b, +} +enum D { + a = (2), + b, +} + + +//// [helpers.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.foo = void 0; +exports.foo = 2; +//// [bad.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var helpers_1 = require("./helpers"); +var A; +(function (A) { + A[A["a"] = 2] = "a"; + A[A["b"] = 3] = "b"; +})(A || (A = {})); +//// [good.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var helpers_1 = require("./helpers"); +var A; +(function (A) { + A[A["a"] = 2] = "a"; + A[A["b"] = 3] = "b"; +})(A || (A = {})); +var B; +(function (B) { + B[B["a"] = 2] = "a"; + B[B["b"] = 3] = "b"; +})(B || (B = {})); +var C; +(function (C) { + C[C["a"] = 2] = "a"; + C[C["b"] = 3] = "b"; +})(C || (C = {})); +var D; +(function (D) { + D[D["a"] = 2] = "a"; + D[D["b"] = 3] = "b"; +})(D || (D = {})); diff --git a/tests/baselines/reference/enumWithNonLiteralStringInitializer.js b/tests/baselines/reference/enumWithNonLiteralStringInitializer.js new file mode 100644 index 0000000000000..a154b0d5950ed --- /dev/null +++ b/tests/baselines/reference/enumWithNonLiteralStringInitializer.js @@ -0,0 +1,41 @@ +//// [tests/cases/compiler/enumWithNonLiteralStringInitializer.ts] //// + +//// [helpers.ts] +export const foo = 2; + +//// [bad.ts] +import { foo } from "./helpers"; +enum A { + a = `${foo}` +} + +//// [good.ts] +enum A { + a = `${"foo"}`, + b = "" + 2, + c = 2 + "", + d = ("foo"), +} + + +//// [helpers.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.foo = void 0; +exports.foo = 2; +//// [bad.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var helpers_1 = require("./helpers"); +var A; +(function (A) { + A["a"] = "2"; +})(A || (A = {})); +//// [good.js] +var A; +(function (A) { + A["a"] = "foo"; + A["b"] = "2"; + A["c"] = "2"; + A["d"] = "foo"; +})(A || (A = {})); diff --git a/tests/cases/compiler/enumNoInitializerFollowsNonLiteralInitializer.ts b/tests/cases/compiler/enumNoInitializerFollowsNonLiteralInitializer.ts index 7f64a8b2eea1c..b4274dbd53385 100644 --- a/tests/cases/compiler/enumNoInitializerFollowsNonLiteralInitializer.ts +++ b/tests/cases/compiler/enumNoInitializerFollowsNonLiteralInitializer.ts @@ -1,5 +1,4 @@ // @isolatedModules: true -// @noEmit: true // @noTypesAndSymbols: true // @filename: ./helpers.ts diff --git a/tests/cases/compiler/enumWithNonLiteralStringInitializer.ts b/tests/cases/compiler/enumWithNonLiteralStringInitializer.ts index ee6002638cab3..23af7d6fd4e01 100644 --- a/tests/cases/compiler/enumWithNonLiteralStringInitializer.ts +++ b/tests/cases/compiler/enumWithNonLiteralStringInitializer.ts @@ -1,5 +1,4 @@ // @isolatedModules: true -// @noEmit: true // @noTypesAndSymbols: true // @filename: ./helpers.ts From e112f4d7efccaae276833334bce0b9b4e1800666 Mon Sep 17 00:00:00 2001 From: Jan Kuehle Date: Thu, 25 Jan 2024 14:43:34 +0000 Subject: [PATCH 05/10] No reverse mapping for syntactically known strings --- src/compiler/checker.ts | 4 ++-- src/compiler/emitter.ts | 1 + src/compiler/transformers/ts.ts | 7 +++++- src/compiler/types.ts | 1 + ...WithNonLiteralStringInitializer.errors.txt | 22 ------------------- 5 files changed, 10 insertions(+), 25 deletions(-) delete mode 100644 tests/baselines/reference/enumWithNonLiteralStringInitializer.errors.txt diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5bd23b5d5d3e2..60390f12b8ba4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -45114,10 +45114,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { (isSyntacticallyString(right) || rightIsNumeric) && (expr as BinaryExpression).operatorToken.kind === SyntaxKind.PlusToken ); - case SyntaxKind.TemplateExpression: - return (expr as TemplateExpression).templateSpans.every(span => isSyntacticallyString(span.expression)); case SyntaxKind.ParenthesizedExpression: return isSyntacticallyString((expr as ParenthesizedExpression).expression); + case SyntaxKind.TemplateExpression: case SyntaxKind.StringLiteral: case SyntaxKind.NoSubstitutionTemplateLiteral: return true; @@ -48297,6 +48296,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const node = getParseTreeNode(nodeIn, canHaveConstantValue); return node ? getConstantValue(node) : undefined; }, + isSyntacticallyString, collectLinkedAliases, getReferencedValueDeclaration, getReferencedValueDeclarations, diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 9e8f97a248c7e..f5f6f9c603c7d 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1158,6 +1158,7 @@ export const notImplementedResolver: EmitResolver = { isEntityNameVisible: notImplemented, // Returns the constant value this property access resolves to: notImplemented, or 'undefined' for a non-constant getConstantValue: notImplemented, + isSyntacticallyString: notImplemented, getReferencedValueDeclaration: notImplemented, getReferencedValueDeclarations: notImplemented, getTypeReferenceSerializationKind: notImplemented, diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 0548a5bf0da9c..2fbafc6fe9d45 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -1873,7 +1873,12 @@ export function transformTypeScript(context: TransformationContext) { ), valueExpression, ); - const outerAssignment = valueExpression.kind === SyntaxKind.StringLiteral ? + const isString = valueExpression.kind === SyntaxKind.StringLiteral || + // Fix ts.transpileModule() emit: we may not have been able to determine a known string due + // to missing type information, but we know syntactically that it's a string. The checker + // ensures that only syntactically determined strings are allowed under isolatedModules. + (member.initializer && resolver.isSyntacticallyString(member.initializer)); + const outerAssignment = isString ? innerAssignment : factory.createAssignment( factory.createElementAccessExpression( diff --git a/src/compiler/types.ts b/src/compiler/types.ts index bc833556d8062..639fa35a83e1d 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -5691,6 +5691,7 @@ export interface EmitResolver { isEntityNameVisible(entityName: EntityNameOrEntityNameExpression, enclosingDeclaration: Node): SymbolVisibilityResult; // Returns the constant value this property access resolves to, or 'undefined' for a non-constant getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): string | number | undefined; + isSyntacticallyString(node: Expression): boolean; getReferencedValueDeclaration(reference: Identifier): Declaration | undefined; getReferencedValueDeclarations(reference: Identifier): Declaration[] | undefined; getTypeReferenceSerializationKind(typeName: EntityName, location?: Node): TypeReferenceSerializationKind; diff --git a/tests/baselines/reference/enumWithNonLiteralStringInitializer.errors.txt b/tests/baselines/reference/enumWithNonLiteralStringInitializer.errors.txt deleted file mode 100644 index a69a0fde02a65..0000000000000 --- a/tests/baselines/reference/enumWithNonLiteralStringInitializer.errors.txt +++ /dev/null @@ -1,22 +0,0 @@ -bad.ts(3,8): error TS18055: A string member initializer in a enum declaration can only use constant expressions when 'isolatedModules' is enabled. - - -==== ./helpers.ts (0 errors) ==== - export const foo = 2; - -==== ./bad.ts (1 errors) ==== - import { foo } from "./helpers"; - enum A { - a = `${foo}` - ~~~~~~~~ -!!! error TS18055: A string member initializer in a enum declaration can only use constant expressions when 'isolatedModules' is enabled. - } - -==== ./good.ts (0 errors) ==== - enum A { - a = `${"foo"}`, - b = "" + 2, - c = 2 + "", - d = ("foo"), - } - \ No newline at end of file From dd647aa9530a4d1b57d6334e7f56cdeb82d5dcd5 Mon Sep 17 00:00:00 2001 From: Jan Kuehle Date: Fri, 15 Mar 2024 08:38:36 +0000 Subject: [PATCH 06/10] Revert "No reverse mapping for syntactically known strings" This reverts commit e112f4d7efccaae276833334bce0b9b4e1800666. This has been implemented in f9ef9439bd02e1895991d71d2277d3214a7871f3 --- src/compiler/checker.ts | 4 ++-- src/compiler/emitter.ts | 1 - src/compiler/transformers/ts.ts | 7 +----- src/compiler/types.ts | 1 - ...WithNonLiteralStringInitializer.errors.txt | 22 +++++++++++++++++++ 5 files changed, 25 insertions(+), 10 deletions(-) create mode 100644 tests/baselines/reference/enumWithNonLiteralStringInitializer.errors.txt diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 60390f12b8ba4..5bd23b5d5d3e2 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -45114,9 +45114,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { (isSyntacticallyString(right) || rightIsNumeric) && (expr as BinaryExpression).operatorToken.kind === SyntaxKind.PlusToken ); + case SyntaxKind.TemplateExpression: + return (expr as TemplateExpression).templateSpans.every(span => isSyntacticallyString(span.expression)); case SyntaxKind.ParenthesizedExpression: return isSyntacticallyString((expr as ParenthesizedExpression).expression); - case SyntaxKind.TemplateExpression: case SyntaxKind.StringLiteral: case SyntaxKind.NoSubstitutionTemplateLiteral: return true; @@ -48296,7 +48297,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const node = getParseTreeNode(nodeIn, canHaveConstantValue); return node ? getConstantValue(node) : undefined; }, - isSyntacticallyString, collectLinkedAliases, getReferencedValueDeclaration, getReferencedValueDeclarations, diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index f5f6f9c603c7d..9e8f97a248c7e 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1158,7 +1158,6 @@ export const notImplementedResolver: EmitResolver = { isEntityNameVisible: notImplemented, // Returns the constant value this property access resolves to: notImplemented, or 'undefined' for a non-constant getConstantValue: notImplemented, - isSyntacticallyString: notImplemented, getReferencedValueDeclaration: notImplemented, getReferencedValueDeclarations: notImplemented, getTypeReferenceSerializationKind: notImplemented, diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 2fbafc6fe9d45..0548a5bf0da9c 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -1873,12 +1873,7 @@ export function transformTypeScript(context: TransformationContext) { ), valueExpression, ); - const isString = valueExpression.kind === SyntaxKind.StringLiteral || - // Fix ts.transpileModule() emit: we may not have been able to determine a known string due - // to missing type information, but we know syntactically that it's a string. The checker - // ensures that only syntactically determined strings are allowed under isolatedModules. - (member.initializer && resolver.isSyntacticallyString(member.initializer)); - const outerAssignment = isString ? + const outerAssignment = valueExpression.kind === SyntaxKind.StringLiteral ? innerAssignment : factory.createAssignment( factory.createElementAccessExpression( diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 639fa35a83e1d..bc833556d8062 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -5691,7 +5691,6 @@ export interface EmitResolver { isEntityNameVisible(entityName: EntityNameOrEntityNameExpression, enclosingDeclaration: Node): SymbolVisibilityResult; // Returns the constant value this property access resolves to, or 'undefined' for a non-constant getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): string | number | undefined; - isSyntacticallyString(node: Expression): boolean; getReferencedValueDeclaration(reference: Identifier): Declaration | undefined; getReferencedValueDeclarations(reference: Identifier): Declaration[] | undefined; getTypeReferenceSerializationKind(typeName: EntityName, location?: Node): TypeReferenceSerializationKind; diff --git a/tests/baselines/reference/enumWithNonLiteralStringInitializer.errors.txt b/tests/baselines/reference/enumWithNonLiteralStringInitializer.errors.txt new file mode 100644 index 0000000000000..a69a0fde02a65 --- /dev/null +++ b/tests/baselines/reference/enumWithNonLiteralStringInitializer.errors.txt @@ -0,0 +1,22 @@ +bad.ts(3,8): error TS18055: A string member initializer in a enum declaration can only use constant expressions when 'isolatedModules' is enabled. + + +==== ./helpers.ts (0 errors) ==== + export const foo = 2; + +==== ./bad.ts (1 errors) ==== + import { foo } from "./helpers"; + enum A { + a = `${foo}` + ~~~~~~~~ +!!! error TS18055: A string member initializer in a enum declaration can only use constant expressions when 'isolatedModules' is enabled. + } + +==== ./good.ts (0 errors) ==== + enum A { + a = `${"foo"}`, + b = "" + 2, + c = 2 + "", + d = ("foo"), + } + \ No newline at end of file From 42eb9b14484968664446bdf4ee43a186cb28f71e Mon Sep 17 00:00:00 2001 From: Jan Kuehle Date: Fri, 15 Mar 2024 09:20:40 +0000 Subject: [PATCH 07/10] Update based on #57686 --- src/compiler/checker.ts | 40 +------------------ src/compiler/utilities.ts | 14 +++++++ ...WithNonLiteralStringInitializer.errors.txt | 10 +++-- .../enumWithNonLiteralStringInitializer.js | 18 ++++++--- .../enumWithNonLiteralStringInitializer.ts | 8 ++-- 5 files changed, 39 insertions(+), 51 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 966c1b47ccb1f..ea84063e2ebaa 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -719,6 +719,8 @@ import { isStringOrNumericLiteralLike, isSuperCall, isSuperProperty, + isSyntacticallyNumeric, + isSyntacticallyString, isTaggedTemplateExpression, isTemplateSpan, isThisContainerOrFunctionBlock, @@ -45595,44 +45597,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return value; } - function isSyntacticallyNumeric(expr: Expression): boolean { - switch (expr.kind) { - case SyntaxKind.PrefixUnaryExpression: - return isSyntacticallyNumeric((expr as PrefixUnaryExpression).operand); - case SyntaxKind.BinaryExpression: - return isSyntacticallyNumeric((expr as BinaryExpression).left) && isSyntacticallyNumeric((expr as BinaryExpression).right); - case SyntaxKind.ParenthesizedExpression: - return isSyntacticallyNumeric((expr as ParenthesizedExpression).expression); - case SyntaxKind.NumericLiteral: - return true; - } - return false; - } - - function isSyntacticallyString(expr: Expression): boolean { - switch (expr.kind) { - case SyntaxKind.BinaryExpression: - const left = (expr as BinaryExpression).left; - const right = (expr as BinaryExpression).right; - const leftIsNumeric = isSyntacticallyNumeric(left); - const rightIsNumeric = isSyntacticallyNumeric(right); - return ( - !(leftIsNumeric && rightIsNumeric) && - (isSyntacticallyString(left) || leftIsNumeric) && - (isSyntacticallyString(right) || rightIsNumeric) && - (expr as BinaryExpression).operatorToken.kind === SyntaxKind.PlusToken - ); - case SyntaxKind.TemplateExpression: - return (expr as TemplateExpression).templateSpans.every(span => isSyntacticallyString(span.expression)); - case SyntaxKind.ParenthesizedExpression: - return isSyntacticallyString((expr as ParenthesizedExpression).expression); - case SyntaxKind.StringLiteral: - case SyntaxKind.NoSubstitutionTemplateLiteral: - return true; - } - return false; - } - function evaluate(expr: Expression, location?: Declaration): string | number | undefined { switch (expr.kind) { case SyntaxKind.PrefixUnaryExpression: diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index e6d6ac12125a3..700de47d9cd03 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -10657,3 +10657,17 @@ export function isSyntacticallyString(expr: Expression): boolean { } return false; } + +/** @internal */ +export function isSyntacticallyNumeric(expr: Expression): boolean { + expr = skipOuterExpressions(expr); + switch (expr.kind) { + case SyntaxKind.PrefixUnaryExpression: + return isSyntacticallyNumeric((expr as PrefixUnaryExpression).operand); + case SyntaxKind.BinaryExpression: + return isSyntacticallyNumeric((expr as BinaryExpression).left) && isSyntacticallyNumeric((expr as BinaryExpression).right); + case SyntaxKind.NumericLiteral: + return true; + } + return false; +} diff --git a/tests/baselines/reference/enumWithNonLiteralStringInitializer.errors.txt b/tests/baselines/reference/enumWithNonLiteralStringInitializer.errors.txt index a69a0fde02a65..b9d5844f810a1 100644 --- a/tests/baselines/reference/enumWithNonLiteralStringInitializer.errors.txt +++ b/tests/baselines/reference/enumWithNonLiteralStringInitializer.errors.txt @@ -3,18 +3,20 @@ bad.ts(3,8): error TS18055: A string member initializer in a enum declaration ca ==== ./helpers.ts (0 errors) ==== export const foo = 2; + export const bar = "bar"; ==== ./bad.ts (1 errors) ==== - import { foo } from "./helpers"; + import { bar } from "./helpers"; enum A { - a = `${foo}` - ~~~~~~~~ + a = bar, + ~~~ !!! error TS18055: A string member initializer in a enum declaration can only use constant expressions when 'isolatedModules' is enabled. } ==== ./good.ts (0 errors) ==== + import { foo } from "./helpers"; enum A { - a = `${"foo"}`, + a = `${foo}`, b = "" + 2, c = 2 + "", d = ("foo"), diff --git a/tests/baselines/reference/enumWithNonLiteralStringInitializer.js b/tests/baselines/reference/enumWithNonLiteralStringInitializer.js index a154b0d5950ed..a9812bf95e169 100644 --- a/tests/baselines/reference/enumWithNonLiteralStringInitializer.js +++ b/tests/baselines/reference/enumWithNonLiteralStringInitializer.js @@ -2,16 +2,18 @@ //// [helpers.ts] export const foo = 2; +export const bar = "bar"; //// [bad.ts] -import { foo } from "./helpers"; +import { bar } from "./helpers"; enum A { - a = `${foo}` + a = bar, } //// [good.ts] +import { foo } from "./helpers"; enum A { - a = `${"foo"}`, + a = `${foo}`, b = "" + 2, c = 2 + "", d = ("foo"), @@ -21,20 +23,24 @@ enum A { //// [helpers.js] "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.foo = void 0; +exports.bar = exports.foo = void 0; exports.foo = 2; +exports.bar = "bar"; //// [bad.js] "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var helpers_1 = require("./helpers"); var A; (function (A) { - A["a"] = "2"; + A["a"] = "bar"; })(A || (A = {})); //// [good.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var helpers_1 = require("./helpers"); var A; (function (A) { - A["a"] = "foo"; + A["a"] = "2"; A["b"] = "2"; A["c"] = "2"; A["d"] = "foo"; diff --git a/tests/cases/compiler/enumWithNonLiteralStringInitializer.ts b/tests/cases/compiler/enumWithNonLiteralStringInitializer.ts index 23af7d6fd4e01..d942313c4eaf1 100644 --- a/tests/cases/compiler/enumWithNonLiteralStringInitializer.ts +++ b/tests/cases/compiler/enumWithNonLiteralStringInitializer.ts @@ -3,16 +3,18 @@ // @filename: ./helpers.ts export const foo = 2; +export const bar = "bar"; // @filename: ./bad.ts -import { foo } from "./helpers"; +import { bar } from "./helpers"; enum A { - a = `${foo}` + a = bar, } // @filename: ./good.ts +import { foo } from "./helpers"; enum A { - a = `${"foo"}`, + a = `${foo}`, b = "" + 2, c = 2 + "", d = ("foo"), From dafdaecb7cee8f09e973863a8e142d685eaf3c0f Mon Sep 17 00:00:00 2001 From: Jan Kuehle Date: Tue, 19 Mar 2024 09:00:38 +0000 Subject: [PATCH 08/10] Rename to isSyntacticallyNumericConstant --- src/compiler/checker.ts | 16 ++++++++++++++-- src/compiler/utilities.ts | 14 -------------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ea84063e2ebaa..796243d36d6ce 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -719,7 +719,6 @@ import { isStringOrNumericLiteralLike, isSuperCall, isSuperProperty, - isSyntacticallyNumeric, isSyntacticallyString, isTaggedTemplateExpression, isTemplateSpan, @@ -45556,7 +45555,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { error(member.name, Diagnostics.Enum_member_must_have_initializer); return undefined; } - if (getIsolatedModules(compilerOptions) && previous?.initializer && !isSyntacticallyNumeric(previous.initializer)) { + if (getIsolatedModules(compilerOptions) && previous?.initializer && !isSyntacticallyNumericConstant(previous.initializer)) { error( member.name, Diagnostics.Enum_member_following_a_non_literal_numeric_member_must_have_an_initializer_when_isolatedModules_is_enabled, @@ -45597,6 +45596,19 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return value; } + function isSyntacticallyNumericConstant(expr: Expression): boolean { + expr = skipOuterExpressions(expr); + switch (expr.kind) { + case SyntaxKind.PrefixUnaryExpression: + return isSyntacticallyNumericConstant((expr as PrefixUnaryExpression).operand); + case SyntaxKind.BinaryExpression: + return isSyntacticallyNumericConstant((expr as BinaryExpression).left) && isSyntacticallyNumericConstant((expr as BinaryExpression).right); + case SyntaxKind.NumericLiteral: + return true; + } + return false; + } + function evaluate(expr: Expression, location?: Declaration): string | number | undefined { switch (expr.kind) { case SyntaxKind.PrefixUnaryExpression: diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 700de47d9cd03..e6d6ac12125a3 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -10657,17 +10657,3 @@ export function isSyntacticallyString(expr: Expression): boolean { } return false; } - -/** @internal */ -export function isSyntacticallyNumeric(expr: Expression): boolean { - expr = skipOuterExpressions(expr); - switch (expr.kind) { - case SyntaxKind.PrefixUnaryExpression: - return isSyntacticallyNumeric((expr as PrefixUnaryExpression).operand); - case SyntaxKind.BinaryExpression: - return isSyntacticallyNumeric((expr as BinaryExpression).left) && isSyntacticallyNumeric((expr as BinaryExpression).right); - case SyntaxKind.NumericLiteral: - return true; - } - return false; -} From bd28afa0d0ecd2fa0aa956f2079526c210487a1b Mon Sep 17 00:00:00 2001 From: Jan Kuehle Date: Tue, 19 Mar 2024 18:14:52 +0000 Subject: [PATCH 09/10] Update string error message --- src/compiler/checker.ts | 3 ++- src/compiler/diagnosticMessages.json | 2 +- .../reference/enumWithNonLiteralStringInitializer.errors.txt | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 796243d36d6ce..89fc8bcc525a1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -45580,7 +45580,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { else if (getIsolatedModules(compilerOptions) && typeof value === "string" && !isSyntacticallyString(initializer)) { error( initializer, - Diagnostics.A_string_member_initializer_in_a_enum_declaration_can_only_use_constant_expressions_when_isolatedModules_is_enabled, + Diagnostics._0_has_a_string_type_but_must_have_syntactically_recognizable_string_syntax_when_isolatedModules_is_enabled, + `${idText(member.parent.name)}.${getTextOfPropertyName(member.name)}`, ); } } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index e2d143130fb97..dfbb41b3d409d 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -7977,7 +7977,7 @@ "category": "Error", "code": 18054 }, - "A string member initializer in a enum declaration can only use constant expressions when 'isolatedModules' is enabled.": { + "'{0}' has a string type, but must have syntactically recognizable string syntax when 'isolatedModules' is enabled.": { "category": "Error", "code": 18055 }, diff --git a/tests/baselines/reference/enumWithNonLiteralStringInitializer.errors.txt b/tests/baselines/reference/enumWithNonLiteralStringInitializer.errors.txt index b9d5844f810a1..0c6a54a6bc8d4 100644 --- a/tests/baselines/reference/enumWithNonLiteralStringInitializer.errors.txt +++ b/tests/baselines/reference/enumWithNonLiteralStringInitializer.errors.txt @@ -1,4 +1,4 @@ -bad.ts(3,8): error TS18055: A string member initializer in a enum declaration can only use constant expressions when 'isolatedModules' is enabled. +bad.ts(3,8): error TS18055: 'A.a' has a string type, but must have syntactically recognizable string syntax when 'isolatedModules' is enabled. ==== ./helpers.ts (0 errors) ==== @@ -10,7 +10,7 @@ bad.ts(3,8): error TS18055: A string member initializer in a enum declaration ca enum A { a = bar, ~~~ -!!! error TS18055: A string member initializer in a enum declaration can only use constant expressions when 'isolatedModules' is enabled. +!!! error TS18055: 'A.a' has a string type, but must have syntactically recognizable string syntax when 'isolatedModules' is enabled. } ==== ./good.ts (0 errors) ==== From 8b926c92a1eed051ed92e9e2a923983a01a27b72 Mon Sep 17 00:00:00 2001 From: Jan Kuehle Date: Tue, 19 Mar 2024 21:18:19 +0000 Subject: [PATCH 10/10] Update isolatedModulesGlobalNamespacesAndEnums baseline --- .../isolatedModulesGlobalNamespacesAndEnums.errors.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/baselines/reference/isolatedModulesGlobalNamespacesAndEnums.errors.txt b/tests/baselines/reference/isolatedModulesGlobalNamespacesAndEnums.errors.txt index c8c456a221157..d5e4778259d40 100644 --- a/tests/baselines/reference/isolatedModulesGlobalNamespacesAndEnums.errors.txt +++ b/tests/baselines/reference/isolatedModulesGlobalNamespacesAndEnums.errors.txt @@ -1,4 +1,4 @@ -enum2.ts(2,9): error TS18055: A string member initializer in a enum declaration can only use constant expressions when 'isolatedModules' is enabled. +enum2.ts(2,9): error TS18055: 'Enum.D' has a string type, but must have syntactically recognizable string syntax when 'isolatedModules' is enabled. enum2.ts(3,9): error TS1281: Cannot access 'A' from another file without qualification when 'isolatedModules' is enabled. Use 'Enum.A' instead. enum2.ts(4,9): error TS1281: Cannot access 'X' from another file without qualification when 'isolatedModules' is enabled. Use 'Enum.X' instead. script-namespaces.ts(1,11): error TS1280: Namespaces are not allowed in global script files when 'isolatedModules' is enabled. If this file is not intended to be a global script, set 'moduleDetection' to 'force' or add an empty 'export {}' statement. @@ -31,7 +31,7 @@ script-namespaces.ts(1,11): error TS1280: Namespaces are not allowed in global s enum Enum { D = d, ~ -!!! error TS18055: A string member initializer in a enum declaration can only use constant expressions when 'isolatedModules' is enabled. +!!! error TS18055: 'Enum.D' has a string type, but must have syntactically recognizable string syntax when 'isolatedModules' is enabled. E = A, // error ~ !!! error TS1281: Cannot access 'A' from another file without qualification when 'isolatedModules' is enabled. Use 'Enum.A' instead.