mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-04 12:32:08 -06:00
isolatedModules errors for non-literal enum initializers (#56736)
This commit is contained in:
parent
af81456247
commit
f70b068b3c
@ -719,6 +719,7 @@ import {
|
||||
isStringOrNumericLiteralLike,
|
||||
isSuperCall,
|
||||
isSuperProperty,
|
||||
isSyntacticallyString,
|
||||
isTaggedTemplateExpression,
|
||||
isTemplateSpan,
|
||||
isThisContainerOrFunctionBlock,
|
||||
@ -45675,15 +45676,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);
|
||||
}
|
||||
@ -45705,11 +45708,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 (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,
|
||||
);
|
||||
}
|
||||
return autoValue;
|
||||
}
|
||||
|
||||
function computeConstantValue(member: EnumMember): string | number | undefined {
|
||||
@ -45725,6 +45734,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
Diagnostics.const_enum_member_initializer_was_evaluated_to_a_non_finite_value,
|
||||
);
|
||||
}
|
||||
else if (getIsolatedModules(compilerOptions) && typeof value === "string" && !isSyntacticallyString(initializer)) {
|
||||
error(
|
||||
initializer,
|
||||
Diagnostics._0_has_a_string_type_but_must_have_syntactically_recognizable_string_syntax_when_isolatedModules_is_enabled,
|
||||
`${idText(member.parent.name)}.${getTextOfPropertyName(member.name)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
else if (isConstEnum) {
|
||||
error(initializer, Diagnostics.const_enum_member_initializers_must_be_constant_expressions);
|
||||
@ -45738,6 +45754,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:
|
||||
|
||||
@ -7976,5 +7976,13 @@
|
||||
"'await using' statements cannot be used inside a class static block.": {
|
||||
"category": "Error",
|
||||
"code": 18054
|
||||
},
|
||||
"'{0}' has a string type, but must have syntactically recognizable string syntax 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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
@ -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 = {}));
|
||||
@ -0,0 +1,24 @@
|
||||
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) ====
|
||||
export const foo = 2;
|
||||
export const bar = "bar";
|
||||
|
||||
==== ./bad.ts (1 errors) ====
|
||||
import { bar } from "./helpers";
|
||||
enum A {
|
||||
a = bar,
|
||||
~~~
|
||||
!!! error TS18055: 'A.a' has a string type, but must have syntactically recognizable string syntax when 'isolatedModules' is enabled.
|
||||
}
|
||||
|
||||
==== ./good.ts (0 errors) ====
|
||||
import { foo } from "./helpers";
|
||||
enum A {
|
||||
a = `${foo}`,
|
||||
b = "" + 2,
|
||||
c = 2 + "",
|
||||
d = ("foo"),
|
||||
}
|
||||
|
||||
@ -0,0 +1,47 @@
|
||||
//// [tests/cases/compiler/enumWithNonLiteralStringInitializer.ts] ////
|
||||
|
||||
//// [helpers.ts]
|
||||
export const foo = 2;
|
||||
export const bar = "bar";
|
||||
|
||||
//// [bad.ts]
|
||||
import { bar } from "./helpers";
|
||||
enum A {
|
||||
a = bar,
|
||||
}
|
||||
|
||||
//// [good.ts]
|
||||
import { foo } from "./helpers";
|
||||
enum A {
|
||||
a = `${foo}`,
|
||||
b = "" + 2,
|
||||
c = 2 + "",
|
||||
d = ("foo"),
|
||||
}
|
||||
|
||||
|
||||
//// [helpers.js]
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
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"] = "bar";
|
||||
})(A || (A = {}));
|
||||
//// [good.js]
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var helpers_1 = require("./helpers");
|
||||
var A;
|
||||
(function (A) {
|
||||
A["a"] = "2";
|
||||
A["b"] = "2";
|
||||
A["c"] = "2";
|
||||
A["d"] = "foo";
|
||||
})(A || (A = {}));
|
||||
@ -1,3 +1,4 @@
|
||||
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.
|
||||
@ -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: '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.
|
||||
|
||||
@ -0,0 +1,31 @@
|
||||
// @isolatedModules: 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,
|
||||
}
|
||||
21
tests/cases/compiler/enumWithNonLiteralStringInitializer.ts
Normal file
21
tests/cases/compiler/enumWithNonLiteralStringInitializer.ts
Normal file
@ -0,0 +1,21 @@
|
||||
// @isolatedModules: true
|
||||
// @noTypesAndSymbols: true
|
||||
|
||||
// @filename: ./helpers.ts
|
||||
export const foo = 2;
|
||||
export const bar = "bar";
|
||||
|
||||
// @filename: ./bad.ts
|
||||
import { bar } from "./helpers";
|
||||
enum A {
|
||||
a = bar,
|
||||
}
|
||||
|
||||
// @filename: ./good.ts
|
||||
import { foo } from "./helpers";
|
||||
enum A {
|
||||
a = `${foo}`,
|
||||
b = "" + 2,
|
||||
c = 2 + "",
|
||||
d = ("foo"),
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user