From e6bd8587e1d45289e46e94f03b5a94a9e4694c7d Mon Sep 17 00:00:00 2001 From: Caitlin Potter Date: Wed, 5 Mar 2025 16:48:41 -0500 Subject: [PATCH 01/14] WIP -- Add Tests --- .../cases/conformance/asEnum/asEnumBasics.ts | 26 +++++++++++++++++++ .../cases/conformance/asEnum/asEnumSimple.ts | 5 ++++ 2 files changed, 31 insertions(+) create mode 100644 tests/cases/conformance/asEnum/asEnumBasics.ts create mode 100644 tests/cases/conformance/asEnum/asEnumSimple.ts diff --git a/tests/cases/conformance/asEnum/asEnumBasics.ts b/tests/cases/conformance/asEnum/asEnumBasics.ts new file mode 100644 index 0000000000000..e5401ebe920b9 --- /dev/null +++ b/tests/cases/conformance/asEnum/asEnumBasics.ts @@ -0,0 +1,26 @@ +// Enum without initializers have first member = 0 and successive members = N + 1 + +// `as enum` does not introduce auto-incrementing behaviour. +let ExistingShorthand = "exists"; +const E1: enum = { + NonexistingShorthand, // error + ExistingShorthand, // ok + Int: 1, // ok + String: "string", // ok +}; + +// Valid assignments +const nonexist: E1 = E1.NonexistingShorthand; // ok +const exist: E1 = E1.ExistingShorthand; // ok +const ival: E1 = E1.Int; // ok +const sval: E1 = E1.String; // ok + +// Assigning values which are not part of the enum despite being present in the enum +const nonexist_bad: E1 = undefined; // error +const exist_bad: E1 = "exists"; // error +const ival_bad: E1 = 1; // error +const sval_bad: E1 = "string"; // error + +// Assigning values which are not present in the enum +const ival_bad2: E1 = 4; // error +const sval_bad2: E1 = "not string"; // error \ No newline at end of file diff --git a/tests/cases/conformance/asEnum/asEnumSimple.ts b/tests/cases/conformance/asEnum/asEnumSimple.ts new file mode 100644 index 0000000000000..71f945ee33c23 --- /dev/null +++ b/tests/cases/conformance/asEnum/asEnumSimple.ts @@ -0,0 +1,5 @@ +const ENUM: enum = { + a: 1 +}; + +const a: ENUM = 1 \ No newline at end of file From 8e3cd90d5e6b31f9faf822117524863db6b5a2b0 Mon Sep 17 00:00:00 2001 From: Caitlin Potter Date: Thu, 13 Mar 2025 10:13:39 -0400 Subject: [PATCH 02/14] update tests and baselines --- src/compiler/utilitiesPublic.ts | 5 + .../reference/asEnumBasics.errors.txt | 57 +++++ tests/baselines/reference/asEnumBasics.js | 85 +++++++ .../baselines/reference/asEnumBasics.symbols | 141 +++++++++++ tests/baselines/reference/asEnumBasics.types | 227 ++++++++++++++++++ tests/baselines/reference/asEnumSimple.js | 16 ++ .../baselines/reference/asEnumSimple.symbols | 19 ++ tests/baselines/reference/asEnumSimple.types | 29 +++ .../cases/conformance/asEnum/asEnumBasics.ts | 24 +- .../cases/conformance/asEnum/asEnumSimple.ts | 3 +- 10 files changed, 599 insertions(+), 7 deletions(-) create mode 100644 tests/baselines/reference/asEnumBasics.errors.txt create mode 100644 tests/baselines/reference/asEnumBasics.js create mode 100644 tests/baselines/reference/asEnumBasics.symbols create mode 100644 tests/baselines/reference/asEnumBasics.types create mode 100644 tests/baselines/reference/asEnumSimple.js create mode 100644 tests/baselines/reference/asEnumSimple.symbols create mode 100644 tests/baselines/reference/asEnumSimple.types diff --git a/src/compiler/utilitiesPublic.ts b/src/compiler/utilitiesPublic.ts index 02623678a0cf0..61bf000c0069a 100644 --- a/src/compiler/utilitiesPublic.ts +++ b/src/compiler/utilitiesPublic.ts @@ -1426,6 +1426,11 @@ export function isConstTypeReference(node: Node): boolean { node.typeName.escapedText === "const" && !node.typeArguments; } +export function isEnumTypeReference(node: Node): boolean { + return isTypeReferenceNode(node) && isIdentifier(node.typeName) && + node.typeName.escapedText === "enum" && !node.typeArguments; +} + export function skipPartiallyEmittedExpressions(node: Expression): Expression; export function skipPartiallyEmittedExpressions(node: Node): Node; export function skipPartiallyEmittedExpressions(node: Node) { diff --git a/tests/baselines/reference/asEnumBasics.errors.txt b/tests/baselines/reference/asEnumBasics.errors.txt new file mode 100644 index 0000000000000..ca6002f9d48f5 --- /dev/null +++ b/tests/baselines/reference/asEnumBasics.errors.txt @@ -0,0 +1,57 @@ +asEnumBasics.ts(7,5): error TS1061: Enum member must have initializer. +asEnumBasics.ts(21,7): error TS2322: Type '"exists"' is not assignable to type 'E1'. +asEnumBasics.ts(23,7): error TS2322: Type '"string"' is not assignable to type 'E1'. +asEnumBasics.ts(33,33): error TS2345: Argument of type '"exists"' is not assignable to parameter of type 'E1'. +asEnumBasics.ts(38,32): error TS2345: Argument of type '"string"' is not assignable to parameter of type 'E1'. + + +==== asEnumBasics.ts (5 errors) ==== + // Enum without initializers have first member = 0 and successive members = N + 1 + + // `as enum` does not introduce auto-incrementing behaviour. + let ExistingShorthand = "exists"; + const E1: enum = { + NonexistingShorthand, // error -- EnumLiteralExpressions require explicit property definitions. + ExistingShorthand, // error -- EnumLiteralExpressions require explicit property definitions. + ~~~~~~~~~~~~~~~~~ +!!! error TS1061: Enum member must have initializer. + Int: 1, // ok + String: "string", // ok + Flag: 8, // ok + }; + + // Valid assignments + const nonexist: E1 = E1.NonexistingShorthand; // ok + const exist: E1 = E1.ExistingShorthand; // ok + const ival: E1 = E1.Int; // ok + const sval: E1 = E1.String; // ok + + // Assigning values which are not part of the enum despite being present in the enum + const nonexist_bad: E1 = undefined; // error + const exist_bad: E1 = "exists"; // error + ~~~~~~~~~ +!!! error TS2322: Type '"exists"' is not assignable to type 'E1'. + const ival_good: E1 = 1; // ok -- TypeScript is permissive of this in enums, to permit things like bitwise combinations of enum values. + const sval_bad: E1 = "string"; // error + ~~~~~~~~ +!!! error TS2322: Type '"string"' is not assignable to type 'E1'. + + const ival_notpresent: E1 = 4; // ok -- TypeScript is permissive of this in enums, to permit things like bitwise combinations of enum values. + + function functest(value: E1) { + console.log(value); + return value; + } + + const nonexist_bad2: E1 = functest(undefined); // error + const exist_bad2: E1 = functest("exists"); // error + ~~~~~~~~ +!!! error TS2345: Argument of type '"exists"' is not assignable to parameter of type 'E1'. + const ival_good2: E1 = functest(1); // ok + const ival_good3: E1 = functest(4); // ok + const ival_good4: E1 = functest(E1.Int | E1.Flag); // ok + const sval_good2: E1 = functest(E1.String); + const sval_bad2: E1 = functest("string"); // error + ~~~~~~~~ +!!! error TS2345: Argument of type '"string"' is not assignable to parameter of type 'E1'. + \ No newline at end of file diff --git a/tests/baselines/reference/asEnumBasics.js b/tests/baselines/reference/asEnumBasics.js new file mode 100644 index 0000000000000..17a3d250f5d9e --- /dev/null +++ b/tests/baselines/reference/asEnumBasics.js @@ -0,0 +1,85 @@ +//// [tests/cases/conformance/asEnum/asEnumBasics.ts] //// + +//// [asEnumBasics.ts] +// Enum without initializers have first member = 0 and successive members = N + 1 + +// `as enum` does not introduce auto-incrementing behaviour. +let ExistingShorthand = "exists"; +const E1: enum = { + NonexistingShorthand, // error -- EnumLiteralExpressions require explicit property definitions. + ExistingShorthand, // error -- EnumLiteralExpressions require explicit property definitions. + Int: 1, // ok + String: "string", // ok + Flag: 8, // ok +}; + +// Valid assignments +const nonexist: E1 = E1.NonexistingShorthand; // ok +const exist: E1 = E1.ExistingShorthand; // ok +const ival: E1 = E1.Int; // ok +const sval: E1 = E1.String; // ok + +// Assigning values which are not part of the enum despite being present in the enum +const nonexist_bad: E1 = undefined; // error +const exist_bad: E1 = "exists"; // error +const ival_good: E1 = 1; // ok -- TypeScript is permissive of this in enums, to permit things like bitwise combinations of enum values. +const sval_bad: E1 = "string"; // error + +const ival_notpresent: E1 = 4; // ok -- TypeScript is permissive of this in enums, to permit things like bitwise combinations of enum values. + +function functest(value: E1) { + console.log(value); + return value; +} + +const nonexist_bad2: E1 = functest(undefined); // error +const exist_bad2: E1 = functest("exists"); // error +const ival_good2: E1 = functest(1); // ok +const ival_good3: E1 = functest(4); // ok +const ival_good4: E1 = functest(E1.Int | E1.Flag); // ok +const sval_good2: E1 = functest(E1.String); +const sval_bad2: E1 = functest("string"); // error + + +//// [asEnumBasics.js] +// Enum without initializers have first member = 0 and successive members = N + 1 +// `as enum` does not introduce auto-incrementing behaviour. +var ExistingShorthand = "exists"; +var E1 = { + NonexistingShorthand: NonexistingShorthand// Enum without initializers have first member = 0 and successive members = N + 1 + // `as enum` does not introduce auto-incrementing behaviour. + , // Enum without initializers have first member = 0 and successive members = N + 1 + ExistingShorthand: ExistingShorthand// Enum without initializers have first member = 0 and successive members = N + 1 + // `as enum` does not introduce auto-incrementing behaviour. + , // Enum without initializers have first member = 0 and successive members = N + 1 + Int: 1// Enum without initializers have first member = 0 and successive members = N + 1 + // `as enum` does not introduce auto-incrementing behaviour. + , // Enum without initializers have first member = 0 and successive members = N + 1 + String: "string"// Enum without initializers have first member = 0 and successive members = N + 1 + // `as enum` does not introduce auto-incrementing behaviour. + , // Enum without initializers have first member = 0 and successive members = N + 1 + Flag: 8// Enum without initializers have first member = 0 and successive members = N + 1 + // `as enum` does not introduce auto-incrementing behaviour. +}; +// Valid assignments +var nonexist = E1.NonexistingShorthand; // ok +var exist = E1.ExistingShorthand; // ok +var ival = E1.Int; // ok +var sval = E1.String; // ok +// Assigning values which are not part of the enum despite being present in the enum +var nonexist_bad = undefined; // error +var exist_bad = "exists"; // error +var ival_good = 1; // ok -- TypeScript is permissive of this in enums, to permit things like bitwise combinations of enum values. +var sval_bad = "string"; // error +var ival_notpresent = 4; // ok -- TypeScript is permissive of this in enums, to permit things like bitwise combinations of enum values. +function functest(value) { + console.log(value); + return value; +} +var nonexist_bad2 = functest(undefined); // error +var exist_bad2 = functest("exists"); // error +var ival_good2 = functest(1); // ok +var ival_good3 = functest(4); // ok +var ival_good4 = functest(E1.Int | E1.Flag); // ok +var sval_good2 = functest(E1.String); +var sval_bad2 = functest("string"); // error diff --git a/tests/baselines/reference/asEnumBasics.symbols b/tests/baselines/reference/asEnumBasics.symbols new file mode 100644 index 0000000000000..f369909c193db --- /dev/null +++ b/tests/baselines/reference/asEnumBasics.symbols @@ -0,0 +1,141 @@ +//// [tests/cases/conformance/asEnum/asEnumBasics.ts] //// + +=== asEnumBasics.ts === +// Enum without initializers have first member = 0 and successive members = N + 1 + +// `as enum` does not introduce auto-incrementing behaviour. +let ExistingShorthand = "exists"; +>ExistingShorthand : Symbol(ExistingShorthand, Decl(asEnumBasics.ts, 3, 3)) + +const E1: enum = { +>E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) +>E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) + + NonexistingShorthand, // error -- EnumLiteralExpressions require explicit property definitions. +>NonexistingShorthand : Symbol(E1.NonexistingShorthand, Decl(asEnumBasics.ts, 4, 18)) + + ExistingShorthand, // error -- EnumLiteralExpressions require explicit property definitions. +>ExistingShorthand : Symbol(E1.ExistingShorthand, Decl(asEnumBasics.ts, 5, 25)) + + Int: 1, // ok +>Int : Symbol(E1.Int, Decl(asEnumBasics.ts, 6, 22)) + + String: "string", // ok +>String : Symbol(E1.String, Decl(asEnumBasics.ts, 7, 11)) + + Flag: 8, // ok +>Flag : Symbol(E1.Flag, Decl(asEnumBasics.ts, 8, 21)) + +}; + +// Valid assignments +const nonexist: E1 = E1.NonexistingShorthand; // ok +>nonexist : Symbol(nonexist, Decl(asEnumBasics.ts, 13, 5)) +>E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) +>E1.NonexistingShorthand : Symbol(E1.NonexistingShorthand, Decl(asEnumBasics.ts, 4, 18)) +>E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) +>NonexistingShorthand : Symbol(E1.NonexistingShorthand, Decl(asEnumBasics.ts, 4, 18)) + +const exist: E1 = E1.ExistingShorthand; // ok +>exist : Symbol(exist, Decl(asEnumBasics.ts, 14, 5)) +>E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) +>E1.ExistingShorthand : Symbol(E1.ExistingShorthand, Decl(asEnumBasics.ts, 5, 25)) +>E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) +>ExistingShorthand : Symbol(E1.ExistingShorthand, Decl(asEnumBasics.ts, 5, 25)) + +const ival: E1 = E1.Int; // ok +>ival : Symbol(ival, Decl(asEnumBasics.ts, 15, 5)) +>E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) +>E1.Int : Symbol(E1.Int, Decl(asEnumBasics.ts, 6, 22)) +>E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) +>Int : Symbol(E1.Int, Decl(asEnumBasics.ts, 6, 22)) + +const sval: E1 = E1.String; // ok +>sval : Symbol(sval, Decl(asEnumBasics.ts, 16, 5)) +>E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) +>E1.String : Symbol(E1.String, Decl(asEnumBasics.ts, 7, 11)) +>E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) +>String : Symbol(E1.String, Decl(asEnumBasics.ts, 7, 11)) + +// Assigning values which are not part of the enum despite being present in the enum +const nonexist_bad: E1 = undefined; // error +>nonexist_bad : Symbol(nonexist_bad, Decl(asEnumBasics.ts, 19, 5)) +>E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) +>undefined : Symbol(undefined) + +const exist_bad: E1 = "exists"; // error +>exist_bad : Symbol(exist_bad, Decl(asEnumBasics.ts, 20, 5)) +>E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) + +const ival_good: E1 = 1; // ok -- TypeScript is permissive of this in enums, to permit things like bitwise combinations of enum values. +>ival_good : Symbol(ival_good, Decl(asEnumBasics.ts, 21, 5)) +>E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) + +const sval_bad: E1 = "string"; // error +>sval_bad : Symbol(sval_bad, Decl(asEnumBasics.ts, 22, 5)) +>E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) + +const ival_notpresent: E1 = 4; // ok -- TypeScript is permissive of this in enums, to permit things like bitwise combinations of enum values. +>ival_notpresent : Symbol(ival_notpresent, Decl(asEnumBasics.ts, 24, 5)) +>E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) + +function functest(value: E1) { +>functest : Symbol(functest, Decl(asEnumBasics.ts, 24, 30)) +>value : Symbol(value, Decl(asEnumBasics.ts, 26, 18)) +>E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) + + console.log(value); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>value : Symbol(value, Decl(asEnumBasics.ts, 26, 18)) + + return value; +>value : Symbol(value, Decl(asEnumBasics.ts, 26, 18)) +} + +const nonexist_bad2: E1 = functest(undefined); // error +>nonexist_bad2 : Symbol(nonexist_bad2, Decl(asEnumBasics.ts, 31, 5)) +>E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) +>functest : Symbol(functest, Decl(asEnumBasics.ts, 24, 30)) +>undefined : Symbol(undefined) + +const exist_bad2: E1 = functest("exists"); // error +>exist_bad2 : Symbol(exist_bad2, Decl(asEnumBasics.ts, 32, 5)) +>E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) +>functest : Symbol(functest, Decl(asEnumBasics.ts, 24, 30)) + +const ival_good2: E1 = functest(1); // ok +>ival_good2 : Symbol(ival_good2, Decl(asEnumBasics.ts, 33, 5)) +>E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) +>functest : Symbol(functest, Decl(asEnumBasics.ts, 24, 30)) + +const ival_good3: E1 = functest(4); // ok +>ival_good3 : Symbol(ival_good3, Decl(asEnumBasics.ts, 34, 5)) +>E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) +>functest : Symbol(functest, Decl(asEnumBasics.ts, 24, 30)) + +const ival_good4: E1 = functest(E1.Int | E1.Flag); // ok +>ival_good4 : Symbol(ival_good4, Decl(asEnumBasics.ts, 35, 5)) +>E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) +>functest : Symbol(functest, Decl(asEnumBasics.ts, 24, 30)) +>E1.Int : Symbol(E1.Int, Decl(asEnumBasics.ts, 6, 22)) +>E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) +>Int : Symbol(E1.Int, Decl(asEnumBasics.ts, 6, 22)) +>E1.Flag : Symbol(E1.Flag, Decl(asEnumBasics.ts, 8, 21)) +>E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) +>Flag : Symbol(E1.Flag, Decl(asEnumBasics.ts, 8, 21)) + +const sval_good2: E1 = functest(E1.String); +>sval_good2 : Symbol(sval_good2, Decl(asEnumBasics.ts, 36, 5)) +>E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) +>functest : Symbol(functest, Decl(asEnumBasics.ts, 24, 30)) +>E1.String : Symbol(E1.String, Decl(asEnumBasics.ts, 7, 11)) +>E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) +>String : Symbol(E1.String, Decl(asEnumBasics.ts, 7, 11)) + +const sval_bad2: E1 = functest("string"); // error +>sval_bad2 : Symbol(sval_bad2, Decl(asEnumBasics.ts, 37, 5)) +>E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) +>functest : Symbol(functest, Decl(asEnumBasics.ts, 24, 30)) + diff --git a/tests/baselines/reference/asEnumBasics.types b/tests/baselines/reference/asEnumBasics.types new file mode 100644 index 0000000000000..ab94aba567ac2 --- /dev/null +++ b/tests/baselines/reference/asEnumBasics.types @@ -0,0 +1,227 @@ +//// [tests/cases/conformance/asEnum/asEnumBasics.ts] //// + +=== asEnumBasics.ts === +// Enum without initializers have first member = 0 and successive members = N + 1 + +// `as enum` does not introduce auto-incrementing behaviour. +let ExistingShorthand = "exists"; +>ExistingShorthand : string +> : ^^^^^^ +>"exists" : "exists" +> : ^^^^^^^^ + +const E1: enum = { +>E1 : E1 +> : ^^ +>E1 : E1 +> : ^^ + + NonexistingShorthand, // error -- EnumLiteralExpressions require explicit property definitions. +>NonexistingShorthand : E1.NonexistingShorthand +> : ^^^^^^^^^^^^^^^^^^^^^^^ + + ExistingShorthand, // error -- EnumLiteralExpressions require explicit property definitions. +>ExistingShorthand : E1.ExistingShorthand +> : ^^^^^^^^^^^^^^^^^^^^ + + Int: 1, // ok +>Int : E1.Int +> : ^^^^^^ +>1 : 1 +> : ^ + + String: "string", // ok +>String : E1.String +> : ^^^^^^^^^ +>"string" : "string" +> : ^^^^^^^^ + + Flag: 8, // ok +>Flag : E1.Flag +> : ^^^^^^^ +>8 : 8 +> : ^ + +}; + +// Valid assignments +const nonexist: E1 = E1.NonexistingShorthand; // ok +>nonexist : E1 +> : ^^ +>E1.NonexistingShorthand : E1.NonexistingShorthand +> : ^^^^^^^^^^^^^^^^^^^^^^^ +>E1 : typeof E1 +> : ^^^^^^^^^ +>NonexistingShorthand : E1.NonexistingShorthand +> : ^^^^^^^^^^^^^^^^^^^^^^^ + +const exist: E1 = E1.ExistingShorthand; // ok +>exist : E1 +> : ^^ +>E1.ExistingShorthand : E1.ExistingShorthand +> : ^^^^^^^^^^^^^^^^^^^^ +>E1 : typeof E1 +> : ^^^^^^^^^ +>ExistingShorthand : E1.ExistingShorthand +> : ^^^^^^^^^^^^^^^^^^^^ + +const ival: E1 = E1.Int; // ok +>ival : E1 +> : ^^ +>E1.Int : E1.Int +> : ^^^^^^ +>E1 : typeof E1 +> : ^^^^^^^^^ +>Int : E1.Int +> : ^^^^^^ + +const sval: E1 = E1.String; // ok +>sval : E1 +> : ^^ +>E1.String : E1.String +> : ^^^^^^^^^ +>E1 : typeof E1 +> : ^^^^^^^^^ +>String : E1.String +> : ^^^^^^^^^ + +// Assigning values which are not part of the enum despite being present in the enum +const nonexist_bad: E1 = undefined; // error +>nonexist_bad : E1 +> : ^^ +>undefined : undefined +> : ^^^^^^^^^ + +const exist_bad: E1 = "exists"; // error +>exist_bad : E1 +> : ^^ +>"exists" : "exists" +> : ^^^^^^^^ + +const ival_good: E1 = 1; // ok -- TypeScript is permissive of this in enums, to permit things like bitwise combinations of enum values. +>ival_good : E1 +> : ^^ +>1 : 1 +> : ^ + +const sval_bad: E1 = "string"; // error +>sval_bad : E1 +> : ^^ +>"string" : "string" +> : ^^^^^^^^ + +const ival_notpresent: E1 = 4; // ok -- TypeScript is permissive of this in enums, to permit things like bitwise combinations of enum values. +>ival_notpresent : E1 +> : ^^ +>4 : 4 +> : ^ + +function functest(value: E1) { +>functest : (value: E1) => E1 +> : ^ ^^^^^^^^^^^ +>value : E1 +> : ^^ + + console.log(value); +>console.log(value) : void +> : ^^^^ +>console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>console : Console +> : ^^^^^^^ +>log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>value : E1 +> : ^^ + + return value; +>value : E1 +> : ^^ +} + +const nonexist_bad2: E1 = functest(undefined); // error +>nonexist_bad2 : E1 +> : ^^ +>functest(undefined) : E1 +> : ^^ +>functest : (value: E1) => E1 +> : ^ ^^^^^^^^^^^ +>undefined : undefined +> : ^^^^^^^^^ + +const exist_bad2: E1 = functest("exists"); // error +>exist_bad2 : E1 +> : ^^ +>functest("exists") : E1 +> : ^^ +>functest : (value: E1) => E1 +> : ^ ^^^^^^^^^^^ +>"exists" : "exists" +> : ^^^^^^^^ + +const ival_good2: E1 = functest(1); // ok +>ival_good2 : E1 +> : ^^ +>functest(1) : E1 +> : ^^ +>functest : (value: E1) => E1 +> : ^ ^^^^^^^^^^^ +>1 : 1 +> : ^ + +const ival_good3: E1 = functest(4); // ok +>ival_good3 : E1 +> : ^^ +>functest(4) : E1 +> : ^^ +>functest : (value: E1) => E1 +> : ^ ^^^^^^^^^^^ +>4 : 4 +> : ^ + +const ival_good4: E1 = functest(E1.Int | E1.Flag); // ok +>ival_good4 : E1 +> : ^^ +>functest(E1.Int | E1.Flag) : E1 +> : ^^ +>functest : (value: E1) => E1 +> : ^ ^^^^^^^^^^^ +>E1.Int | E1.Flag : number +> : ^^^^^^ +>E1.Int : E1.Int +> : ^^^^^^ +>E1 : typeof E1 +> : ^^^^^^^^^ +>Int : E1.Int +> : ^^^^^^ +>E1.Flag : E1.Flag +> : ^^^^^^^ +>E1 : typeof E1 +> : ^^^^^^^^^ +>Flag : E1.Flag +> : ^^^^^^^ + +const sval_good2: E1 = functest(E1.String); +>sval_good2 : E1 +> : ^^ +>functest(E1.String) : E1 +> : ^^ +>functest : (value: E1) => E1 +> : ^ ^^^^^^^^^^^ +>E1.String : E1.String +> : ^^^^^^^^^ +>E1 : typeof E1 +> : ^^^^^^^^^ +>String : E1.String +> : ^^^^^^^^^ + +const sval_bad2: E1 = functest("string"); // error +>sval_bad2 : E1 +> : ^^ +>functest("string") : E1 +> : ^^ +>functest : (value: E1) => E1 +> : ^ ^^^^^^^^^^^ +>"string" : "string" +> : ^^^^^^^^ + diff --git a/tests/baselines/reference/asEnumSimple.js b/tests/baselines/reference/asEnumSimple.js new file mode 100644 index 0000000000000..64d170cd77c28 --- /dev/null +++ b/tests/baselines/reference/asEnumSimple.js @@ -0,0 +1,16 @@ +//// [tests/cases/conformance/asEnum/asEnumSimple.ts] //// + +//// [asEnumSimple.ts] +const ENUM: enum = { + a: 1, + b: 2 +}; + +const a: ENUM = 1 + +//// [asEnumSimple.js] +var ENUM = { + a: 1, + b: 2 +}; +var a = 1; diff --git a/tests/baselines/reference/asEnumSimple.symbols b/tests/baselines/reference/asEnumSimple.symbols new file mode 100644 index 0000000000000..89db315f5f67f --- /dev/null +++ b/tests/baselines/reference/asEnumSimple.symbols @@ -0,0 +1,19 @@ +//// [tests/cases/conformance/asEnum/asEnumSimple.ts] //// + +=== asEnumSimple.ts === +const ENUM: enum = { +>ENUM : Symbol(ENUM, Decl(asEnumSimple.ts, 0, 5), Decl(asEnumSimple.ts, 0, 5)) +>ENUM : Symbol(ENUM, Decl(asEnumSimple.ts, 0, 5), Decl(asEnumSimple.ts, 0, 5)) + + a: 1, +>a : Symbol(ENUM.a, Decl(asEnumSimple.ts, 0, 20)) + + b: 2 +>b : Symbol(ENUM.b, Decl(asEnumSimple.ts, 1, 7)) + +}; + +const a: ENUM = 1 +>a : Symbol(a, Decl(asEnumSimple.ts, 5, 5)) +>ENUM : Symbol(ENUM, Decl(asEnumSimple.ts, 0, 5), Decl(asEnumSimple.ts, 0, 5)) + diff --git a/tests/baselines/reference/asEnumSimple.types b/tests/baselines/reference/asEnumSimple.types new file mode 100644 index 0000000000000..21c11261f3cac --- /dev/null +++ b/tests/baselines/reference/asEnumSimple.types @@ -0,0 +1,29 @@ +//// [tests/cases/conformance/asEnum/asEnumSimple.ts] //// + +=== asEnumSimple.ts === +const ENUM: enum = { +>ENUM : ENUM +> : ^^^^ +>ENUM : ENUM +> : ^^^^ + + a: 1, +>a : ENUM.a +> : ^^^^^^ +>1 : 1 +> : ^ + + b: 2 +>b : ENUM.b +> : ^^^^^^ +>2 : 2 +> : ^ + +}; + +const a: ENUM = 1 +>a : ENUM +> : ^^^^ +>1 : 1 +> : ^ + diff --git a/tests/cases/conformance/asEnum/asEnumBasics.ts b/tests/cases/conformance/asEnum/asEnumBasics.ts index e5401ebe920b9..e0f624cf0f560 100644 --- a/tests/cases/conformance/asEnum/asEnumBasics.ts +++ b/tests/cases/conformance/asEnum/asEnumBasics.ts @@ -3,10 +3,11 @@ // `as enum` does not introduce auto-incrementing behaviour. let ExistingShorthand = "exists"; const E1: enum = { - NonexistingShorthand, // error - ExistingShorthand, // ok + NonexistingShorthand, // error -- EnumLiteralExpressions require explicit property definitions. + ExistingShorthand, // error -- EnumLiteralExpressions require explicit property definitions. Int: 1, // ok String: "string", // ok + Flag: 8, // ok }; // Valid assignments @@ -18,9 +19,20 @@ const sval: E1 = E1.String; // ok // Assigning values which are not part of the enum despite being present in the enum const nonexist_bad: E1 = undefined; // error const exist_bad: E1 = "exists"; // error -const ival_bad: E1 = 1; // error +const ival_good: E1 = 1; // ok -- TypeScript is permissive of this in enums, to permit things like bitwise combinations of enum values. const sval_bad: E1 = "string"; // error -// Assigning values which are not present in the enum -const ival_bad2: E1 = 4; // error -const sval_bad2: E1 = "not string"; // error \ No newline at end of file +const ival_notpresent: E1 = 4; // ok -- TypeScript is permissive of this in enums, to permit things like bitwise combinations of enum values. + +function functest(value: E1) { + console.log(value); + return value; +} + +const nonexist_bad2: E1 = functest(undefined); // error +const exist_bad2: E1 = functest("exists"); // error +const ival_good2: E1 = functest(1); // ok +const ival_good3: E1 = functest(4); // ok +const ival_good4: E1 = functest(E1.Int | E1.Flag); // ok +const sval_good2: E1 = functest(E1.String); +const sval_bad2: E1 = functest("string"); // error diff --git a/tests/cases/conformance/asEnum/asEnumSimple.ts b/tests/cases/conformance/asEnum/asEnumSimple.ts index 71f945ee33c23..cf52b22b27aad 100644 --- a/tests/cases/conformance/asEnum/asEnumSimple.ts +++ b/tests/cases/conformance/asEnum/asEnumSimple.ts @@ -1,5 +1,6 @@ const ENUM: enum = { - a: 1 + a: 1, + b: 2 }; const a: ENUM = 1 \ No newline at end of file From 9cb6f7a61f35bf3a4e445045d59e8222e4dff3bd Mon Sep 17 00:00:00 2001 From: Caitlin Potter Date: Thu, 13 Mar 2025 10:14:09 -0400 Subject: [PATCH 03/14] implement enum type annotation and enum literal syntax --- src/compiler/binder.ts | 32 ++++++++++++++ src/compiler/checker.ts | 41 ++++++++++++++--- src/compiler/emitter.ts | 24 ++++++++++ src/compiler/factory/nodeFactory.ts | 39 +++++++++++++++++ src/compiler/factory/nodeTests.ts | 5 +++ src/compiler/parser.ts | 68 ++++++++++++++++++++++++++--- src/compiler/transformers/ts.ts | 28 +++++++++++- src/compiler/types.ts | 20 ++++++++- src/compiler/utilities.ts | 8 +++- src/compiler/utilitiesPublic.ts | 11 +++++ src/compiler/visitorPublic.ts | 10 +++++ 11 files changed, 271 insertions(+), 15 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index c83e11fc2a4a0..690ce2dd1f639 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -57,6 +57,7 @@ import { ElementAccessExpression, EntityNameExpression, EnumDeclaration, + EnumLiteralExpression, escapeLeadingUnderscores, every, ExportAssignment, @@ -84,6 +85,7 @@ import { FunctionLikeDeclaration, GetAccessorDeclaration, getAssignedExpandoInitializer, + getAssignedName, getAssignmentDeclarationKind, getAssignmentDeclarationPropertyAccessKind, getCombinedModifierFlags, @@ -155,6 +157,8 @@ import { isEmptyObjectLiteral, isEntityNameExpression, isEnumConst, + isEnumLiteralExpression, + isEnumTypeReference, isExportAssignment, isExportDeclaration, isExportsIdentifier, @@ -822,6 +826,9 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void { if (isNamedDeclaration(node)) { setParent(node.name, node); } + if (isEnumLiteralExpression(node) && symbol.valueDeclaration === node.parent) { + // This is not a real redeclaration, but is in fact two separate meanings for the same symbol. + } // Report errors every position with duplicate declaration // Report errors on previous encountered declarations let message = symbol.flags & SymbolFlags.BlockScopedVariable @@ -2272,6 +2279,7 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void { case SyntaxKind.ClassDeclaration: return declareClassMember(node, symbolFlags, symbolExcludes); + case SyntaxKind.EnumLiteralExpression: case SyntaxKind.EnumDeclaration: return declareSymbol(container.symbol.exports!, container.symbol, node, symbolFlags, symbolExcludes); @@ -3044,6 +3052,8 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void { return bindBlockScopedDeclaration(node as Declaration, SymbolFlags.Interface, SymbolFlags.InterfaceExcludes); case SyntaxKind.TypeAliasDeclaration: return bindBlockScopedDeclaration(node as Declaration, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes); + case SyntaxKind.EnumLiteralExpression: + return bindEnumLiteralExpression(node as EnumLiteralExpression); case SyntaxKind.EnumDeclaration: return bindEnumDeclaration(node as EnumDeclaration); case SyntaxKind.ModuleDeclaration: @@ -3655,6 +3665,27 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void { : bindBlockScopedDeclaration(node, SymbolFlags.RegularEnum, SymbolFlags.RegularEnumExcludes); } + function bindEnumLiteralExpression(node: EnumLiteralExpression) { + Debug.assert(isVariableDeclaration(node.parent)); + + const varSymbol: Symbol = node.parent.symbol; + node.symbol = varSymbol; + varSymbol.flags |= isEnumConst(node) ? SymbolFlags.ConstEnum : SymbolFlags.RegularEnum; + varSymbol.flags &= ~(SymbolFlags.Assignment | SymbolFlags.Variable); + varSymbol.exports ??= createSymbolTable(); + appendIfUnique(varSymbol.declarations, node); + + // Temporarily treat `node` as the container + // const saveContainer = container; + // container = node; + // bindChildren(node); + // container = saveContainer; + // return isEnumConst(node) + // ? bindBlockScopedDeclaration(node, SymbolFlags.ConstEnum, SymbolFlags.ConstEnumExcludes) + // : bindBlockScopedDeclaration(node, SymbolFlags.RegularEnum, SymbolFlags.RegularEnumExcludes); + } + + function bindVariableDeclarationOrBindingElement(node: VariableDeclaration | BindingElement) { if (inStrictMode) { checkStrictModeEvalOrArguments(node, node.name); @@ -3914,6 +3945,7 @@ export function getContainerFlags(node: Node): ContainerFlags { case SyntaxKind.ClassExpression: case SyntaxKind.ClassDeclaration: case SyntaxKind.EnumDeclaration: + case SyntaxKind.EnumLiteralExpression: case SyntaxKind.ObjectLiteralExpression: case SyntaxKind.TypeLiteral: case SyntaxKind.JSDocTypeLiteral: diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index caab75c99fa8a..2b2d741b3b15d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1133,6 +1133,11 @@ import { WideningContext, WithStatement, YieldExpression, + EnumLiteralExpression, + isEnumLiteralExpression, + isEnumTypeReference, + isEnumTypeAnnotation, + isEnumLiteralDeclaration, } from "./_namespaces/ts.js"; import * as moduleSpecifiers from "./_namespaces/ts.moduleSpecifiers.js"; import * as performance from "./_namespaces/ts.performance.js"; @@ -11984,7 +11989,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } // getTypeOfSymbol dispatches some JS merges incorrectly because their symbol flags are not mutually exclusive. // Re-dispatch based on valueDeclaration.kind instead. - else if (isEnumDeclaration(declaration)) { + else if (isEnumDeclaration(declaration) || isEnumLiteralExpression(declaration)) { type = getTypeOfFuncClassEnumModule(symbol); } else if (isEnumMember(declaration)) { @@ -12876,7 +12881,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const memberTypeList: Type[] = []; if (symbol.declarations) { for (const declaration of symbol.declarations) { - if (declaration.kind === SyntaxKind.EnumDeclaration) { + if (declaration.kind === SyntaxKind.EnumDeclaration || declaration.kind === SyntaxKind.EnumLiteralExpression) { for (const member of (declaration as EnumDeclaration).members) { if (hasBindableName(member)) { const memberSymbol = getSymbolOfDeclaration(member); @@ -16706,6 +16711,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { links.resolvedSymbol = unknownSymbol; return links.resolvedType = checkExpressionCached(node.parent.expression); } + // `var MyEnum: enum = { FirstValue: 1, SecondValue: 2 }` should resolve to a union of the enum values. + if (isEnumTypeReference(node) && isVariableDeclaration(node.parent) && node.parent.initializer && isEnumLiteralExpression(node.parent.initializer)) { + return links.resolvedType = checkExpressionCached(node.parent.initializer); + } let symbol: Symbol | undefined; let type: Type | undefined; const meaning = SymbolFlags.Type; @@ -16760,6 +16769,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { case SyntaxKind.ClassDeclaration: case SyntaxKind.InterfaceDeclaration: case SyntaxKind.EnumDeclaration: + case SyntaxKind.EnumLiteralExpression: return declaration; } } @@ -41109,6 +41119,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return checkArrayLiteral(node as ArrayLiteralExpression, checkMode, forceTuple); case SyntaxKind.ObjectLiteralExpression: return checkObjectLiteral(node as ObjectLiteralExpression, checkMode); + case SyntaxKind.EnumLiteralExpression: + return checkEnumLiteralExpression(node as EnumLiteralExpression, checkMode); case SyntaxKind.PropertyAccessExpression: return checkPropertyAccessExpression(node as PropertyAccessExpression, checkMode); case SyntaxKind.QualifiedName: @@ -44128,7 +44140,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { checkClassNameCollisionWithObject(name); } } - else if (isEnumDeclaration(node)) { + else if (isEnumDeclaration(node) || (isVariableDeclaration(node) && node.initializer && isEnumLiteralExpression(node.initializer))) { checkTypeNameIsReserved(name, Diagnostics.Enum_name_cannot_be_0); } } @@ -47033,16 +47045,18 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } - function computeEnumMemberValues(node: EnumDeclaration) { + function computeEnumMemberValues(node: EnumDeclaration|EnumLiteralExpression) { const nodeLinks = getNodeLinks(node); if (!(nodeLinks.flags & NodeCheckFlags.EnumValuesComputed)) { nodeLinks.flags |= NodeCheckFlags.EnumValuesComputed; - let autoValue: number | undefined = 0; + // EnumLiteralExpressions are essentially plain ObjectLiteralExpressions and can not have computed values. + const hasComputedValues = !isEnumLiteralExpression(node); + let autoValue: number | undefined = hasComputedValues ? undefined : 0; let previous: EnumMember | undefined; for (const member of node.members) { const result = computeEnumMemberValue(member, autoValue, previous); getNodeLinks(member).enumMemberValue = result; - autoValue = typeof result.value === "number" ? result.value + 1 : undefined; + autoValue = (hasComputedValues && typeof result.value === "number") ? result.value + 1 : undefined; previous = member; } } @@ -47191,6 +47205,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { addLazyDiagnostic(() => checkEnumDeclarationWorker(node)); } + function checkEnumLiteralExpression(node: EnumLiteralExpression) { + addLazyDiagnostic(() => checkEnumDeclarationWorker(node as any)); + return getTypeOfSymbol(getSymbolOfDeclaration(node)); + } + function checkEnumDeclarationWorker(node: EnumDeclaration) { // Grammar checking checkGrammarModifiers(node); @@ -48854,6 +48873,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { case SyntaxKind.ModuleDeclaration: copyLocallyVisibleExportSymbols(getSymbolOfDeclaration(location as ModuleDeclaration | SourceFile).exports!, meaning & SymbolFlags.ModuleMember); break; + case SyntaxKind.EnumLiteralExpression: case SyntaxKind.EnumDeclaration: copySymbols(getSymbolOfDeclaration(location as EnumDeclaration).exports!, meaning & SymbolFlags.EnumMember); break; @@ -49304,6 +49324,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // no other meta properties are valid syntax, thus no others should have symbols return undefined; } + else if (isEnumTypeAnnotation(node)) { + // Avoid symbolizing "enum" keywords in type annotations. + return undefined; + } } switch (node.kind) { @@ -49490,6 +49514,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (isDeclarationNameOrImportPropertyName(node)) { const symbol = getSymbolAtLocation(node); if (symbol) { + if (symbol.valueDeclaration && isEnumLiteralDeclaration(symbol.valueDeclaration)) { + return getDeclaredTypeOfEnum(symbol.valueDeclaration.initializer!); + } return getTypeOfSymbol(symbol); } return errorType; @@ -50352,6 +50379,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { case SyntaxKind.PropertyAssignment: case SyntaxKind.ShorthandPropertyAssignment: case SyntaxKind.EnumMember: + case SyntaxKind.EnumLiteralExpression: case SyntaxKind.ObjectLiteralExpression: case SyntaxKind.FunctionDeclaration: case SyntaxKind.FunctionExpression: @@ -51329,6 +51357,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { findFirstModifierExcept(node, SyntaxKind.AwaitKeyword) : find(node.modifiers, isModifier); case SyntaxKind.EnumDeclaration: + case SyntaxKind.EnumLiteralExpression: return findFirstModifierExcept(node, SyntaxKind.ConstKeyword); default: Debug.assertNever(node); diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 01a96579861f4..a331d538323ab 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -79,6 +79,7 @@ import { ensureTrailingDirectorySeparator, EntityName, EnumDeclaration, + EnumLiteralExpression, EnumMember, escapeJsxAttributeString, escapeLeadingUnderscores, @@ -1924,6 +1925,8 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri return emitArrayLiteralExpression(node as ArrayLiteralExpression); case SyntaxKind.ObjectLiteralExpression: return emitObjectLiteralExpression(node as ObjectLiteralExpression); + case SyntaxKind.EnumLiteralExpression: + return emitEnumLiteralExpression(node as EnumLiteralExpression); case SyntaxKind.PropertyAccessExpression: return emitPropertyAccessExpression(node as PropertyAccessExpression); case SyntaxKind.ElementAccessExpression: @@ -2617,6 +2620,27 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri popNameGenerationScope(node); } + function emitEnumLiteralExpression(node: EnumLiteralExpression) { + // pushNameGenerationScope(node); + // forEach(node.members, generateMemberNames); + + // const indentedFlag = getEmitFlags(node) & EmitFlags.Indented; + // if (indentedFlag) { + // increaseIndent(); + // } + + writeSpace(); + writePunctuation("{"); + emitList(node, node.members, ListFormat.EnumMembers); + writePunctuation("}"); + + // if (indentedFlag) { + // decreaseIndent(); + // } + + // popNameGenerationScope(node); + } + function emitPropertyAccessExpression(node: PropertyAccessExpression) { emitExpression(node.expression, parenthesizer.parenthesizeLeftSideOfAccess); const token = node.questionDotToken || setTextRangePosEnd(factory.createToken(SyntaxKind.DotToken) as DotToken, node.expression.end, node.name.pos); diff --git a/src/compiler/factory/nodeFactory.ts b/src/compiler/factory/nodeFactory.ts index 694262eeeb54a..61e4efb9f5034 100644 --- a/src/compiler/factory/nodeFactory.ts +++ b/src/compiler/factory/nodeFactory.ts @@ -77,6 +77,7 @@ import { EndOfFileToken, EntityName, EnumDeclaration, + EnumLiteralExpression, EnumMember, EqualsGreaterThanToken, escapeLeadingUnderscores, @@ -160,6 +161,7 @@ import { isElementAccessChain, isElementAccessExpression, isEnumDeclaration, + isEnumLiteralExpression, isExclamationToken, isExportAssignment, isExportDeclaration, @@ -754,6 +756,8 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode updateTypeAliasDeclaration, createEnumDeclaration, updateEnumDeclaration, + createEnumLiteralExpression, + updateEnumLiteralExpression, createModuleDeclaration, updateModuleDeclaration, createModuleBlock, @@ -4539,6 +4543,40 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode : node; } + // @api + function createEnumLiteralExpression( + modifiers: readonly ModifierLike[] | undefined, + name: string | Identifier, + members: readonly EnumMember[], + ) { + const node = createBaseDeclaration(SyntaxKind.EnumLiteralExpression); + node.modifiers = asNodeArray(modifiers); + node.name = asName(name); + node.members = createNodeArray(members); + node.transformFlags |= propagateChildrenFlags(node.modifiers) | + propagateChildFlags(node.name) | + propagateChildrenFlags(node.members) | + TransformFlags.ContainsTypeScript; + node.transformFlags &= ~TransformFlags.ContainsPossibleTopLevelAwait; // Enum declarations cannot contain `await` + + node.jsDoc = undefined; // initialized by parser (JsDocContainer) + return node; + } + + // @api + function updateEnumLiteralExpression( + node: EnumLiteralExpression, + modifiers: readonly ModifierLike[] | undefined, + name: Identifier, + members: readonly EnumMember[], + ) { + return node.modifiers !== modifiers + || node.name !== name + || node.members !== members + ? update(createEnumLiteralExpression(modifiers, name, members), node) + : node; + } + // @api function createModuleDeclaration( modifiers: readonly ModifierLike[] | undefined, @@ -7086,6 +7124,7 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode isInterfaceDeclaration(node) ? updateInterfaceDeclaration(node, modifierArray, node.name, node.typeParameters, node.heritageClauses, node.members) : isTypeAliasDeclaration(node) ? updateTypeAliasDeclaration(node, modifierArray, node.name, node.typeParameters, node.type) : isEnumDeclaration(node) ? updateEnumDeclaration(node, modifierArray, node.name, node.members) : + isEnumLiteralExpression(node) ? updateEnumLiteralExpression(node, modifierArray, node.name, node.members) : isModuleDeclaration(node) ? updateModuleDeclaration(node, modifierArray, node.name, node.body) : isImportEqualsDeclaration(node) ? updateImportEqualsDeclaration(node, modifierArray, node.isTypeOnly, node.name, node.moduleReference) : isImportDeclaration(node) ? updateImportDeclaration(node, modifierArray, node.importClause, node.moduleSpecifier, node.attributes) : diff --git a/src/compiler/factory/nodeTests.ts b/src/compiler/factory/nodeTests.ts index 8aa4bb02e83d2..d82e0ef811975 100644 --- a/src/compiler/factory/nodeTests.ts +++ b/src/compiler/factory/nodeTests.ts @@ -47,6 +47,7 @@ import { ElementAccessExpression, EmptyStatement, EnumDeclaration, + EnumLiteralExpression, EnumMember, EqualsGreaterThanToken, ExclamationToken, @@ -817,6 +818,10 @@ export function isEnumDeclaration(node: Node): node is EnumDeclaration { return node.kind === SyntaxKind.EnumDeclaration; } +export function isEnumLiteralExpression(node: Node): node is EnumLiteralExpression { + return node.kind === SyntaxKind.EnumLiteralExpression; +} + export function isModuleDeclaration(node: Node): node is ModuleDeclaration { return node.kind === SyntaxKind.ModuleDeclaration; } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 8c69cccba1282..976c644e8509e 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -75,6 +75,7 @@ import { ensureScriptKind, EntityName, EnumDeclaration, + EnumLiteralExpression, EnumMember, ExclamationToken, ExportAssignment, @@ -135,6 +136,7 @@ import { isAssignmentOperator, isAsyncModifier, isClassMemberModifier, + isEnumTypeReference, isExportAssignment, isExportDeclaration, isExportModifier, @@ -327,6 +329,7 @@ import { ScriptKind, ScriptTarget, SetAccessorDeclaration, + setOriginalNode, setParent, setParentRecursive, setTextRange, @@ -919,6 +922,11 @@ const forEachChildTable: ForEachChildTable = { return visitNode(cbNode, node.name) || visitNode(cbNode, node.initializer); }, + [SyntaxKind.EnumLiteralExpression]: function forEachChildInEnumLiteralExpression(node: EnumLiteralExpression, cbNode: (node: Node) => T | undefined, cbNodes?: (nodes: NodeArray) => T | undefined): T | undefined { + return visitNodes(cbNode, cbNodes, node.modifiers) || + visitNode(cbNode, node.name) || + visitNodes(cbNode, cbNodes, node.members); + }, [SyntaxKind.ModuleDeclaration]: function forEachChildInModuleDeclaration(node: ModuleDeclaration, cbNode: (node: Node) => T | undefined, cbNodes?: (nodes: NodeArray) => T | undefined): T | undefined { return visitNodes(cbNode, cbNodes, node.modifiers) || visitNode(cbNode, node.name) || @@ -7087,7 +7095,7 @@ namespace Parser { let variableDeclaration; if (parseOptional(SyntaxKind.OpenParenToken)) { - variableDeclaration = parseVariableDeclaration(); + variableDeclaration = parseVariableDeclaration() as VariableDeclaration; parseExpected(SyntaxKind.CloseParenToken); } else { @@ -7617,11 +7625,14 @@ namespace Parser { return parseBindingIdentifier(privateIdentifierDiagnosticMessage); } - function parseVariableDeclarationAllowExclamation() { - return parseVariableDeclaration(/*allowExclamation*/ true); + // function parseVariableDeclarationAllowExclamation(): VariableDeclaration { + // return parseVariableDeclaration(/*allowExclamation*/ true, /*allowEnumType*/ false); + // } + function parseVariableDeclarationAllowEnumDeclaration(): VariableDeclaration { + return parseVariableDeclaration(/*allowExclamation*/ true, /*allowEnumType*/ true); } - function parseVariableDeclaration(allowExclamation?: boolean): VariableDeclaration { + function parseVariableDeclaration(allowExclamation?: boolean, allowEnumType?: boolean): VariableDeclaration { const pos = getNodePos(); const hasJSDoc = hasPrecedingJSDocComment(); const name = parseIdentifierOrPattern(Diagnostics.Private_identifiers_are_not_allowed_in_variable_declarations); @@ -7633,6 +7644,12 @@ namespace Parser { exclamationToken = parseTokenNode>(); } const type = parseTypeAnnotation(); + if (allowEnumType && type && isEnumTypeReference(type) && name.kind === SyntaxKind.Identifier) { + const initializer = parseInitializerAsEnumLiteralExpression(name, pos); + const node = factoryCreateVariableDeclaration(name, exclamationToken, type, initializer); + return withJSDoc(finishNode(node, pos), hasJSDoc); + } + const initializer = isInOrOfKeyword(token()) ? undefined : parseInitializer(); const node = factoryCreateVariableDeclaration(name, exclamationToken, type, initializer); return withJSDoc(finishNode(node, pos), hasJSDoc); @@ -7684,7 +7701,7 @@ namespace Parser { declarations = parseDelimitedList( ParsingContext.VariableDeclarations, - inForStatementInitializer ? parseVariableDeclaration : parseVariableDeclarationAllowExclamation, + inForStatementInitializer ? parseVariableDeclaration : parseVariableDeclarationAllowEnumDeclaration, ); setDisallowInContext(savedDisallowIn); @@ -8245,6 +8262,35 @@ namespace Parser { return withJSDoc(finishNode(factory.createEnumMember(name, initializer), pos), hasJSDoc); } + function parseObjectLiteralEnumMember(): EnumMember { + // const MyEnum: enum = { name: value } + const pos = getNodePos(); + const hasJSDoc = hasPrecedingJSDocComment(); + const tokenIsIdentifier = isIdentifier(); + const name = parsePropertyName(); + + const isShorthandPropertyAssignment = tokenIsIdentifier && (token() !== SyntaxKind.ColonToken); + let originalNode: ObjectLiteralElementLike; + let initializer: Expression | undefined; + if (isShorthandPropertyAssignment) { + const equalsToken = parseOptionalToken(SyntaxKind.EqualsToken); + const objectAssignmentInitializer = equalsToken ? allowInAnd(() => parseAssignmentExpressionOrHigher(/*allowReturnTypeInArrowFunction*/ true)) : undefined; + const node: Mutable = originalNode = factory.createShorthandPropertyAssignment(name as Identifier, objectAssignmentInitializer); + // Save equals token for error reporting. + // TODO(rbuckton): Consider manufacturing this when we need to report an error as it is otherwise not useful. + node.equalsToken = equalsToken; + } + else { + parseExpected(SyntaxKind.ColonToken); + initializer = allowInAnd(() => parseAssignmentExpressionOrHigher(/*allowReturnTypeInArrowFunction*/ true)); + originalNode = factory.createPropertyAssignment(name, initializer); + } + + const node = withJSDoc(finishNode(factory.createEnumMember(name, initializer), pos), hasJSDoc); + setOriginalNode(node, originalNode); + return node; + } + function parseEnumDeclaration(pos: number, hasJSDoc: boolean, modifiers: NodeArray | undefined): EnumDeclaration { parseExpected(SyntaxKind.EnumKeyword); const name = parseIdentifier(); @@ -8260,6 +8306,18 @@ namespace Parser { return withJSDoc(finishNode(node, pos), hasJSDoc); } + function parseInitializerAsEnumLiteralExpression(name: Identifier, pos: number) { + // A VariableDeclaration with a declared type `enum` expects the initializer to be in a format like an ObjectLiteralExpression, + // with only constant keys and property assignments, which are treated as EnumMembers. + parseExpected(SyntaxKind.EqualsToken); + parseExpected(SyntaxKind.OpenBraceToken); + const members = parseDelimitedList(ParsingContext.ObjectLiteralMembers, parseObjectLiteralEnumMember); + parseExpected(SyntaxKind.CloseBraceToken); + + // Should the variable have the jsdoc, or the enum literal, or both? + return finishNode(factory.createEnumLiteralExpression(/*modifiers*/ undefined, name, members), pos); + } + function parseModuleBlock(): ModuleBlock { const pos = getNodePos(); let statements; diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 75656cf5aefab..506ba343ada7b 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -33,6 +33,7 @@ import { EmitHint, EntityName, EnumDeclaration, + EnumLiteralExpression, EnumMember, ExportAssignment, ExportDeclaration, @@ -85,6 +86,7 @@ import { isElementAccessExpression, isEntityName, isEnumConst, + isEnumMember, isExportAssignment, isExportDeclaration, isExportOrDefaultModifier, @@ -168,6 +170,7 @@ import { setInternalEmitFlags, setOriginalNode, setParent, + setParentRecursive, setSourceMapRange, setSyntheticLeadingComments, setSyntheticTrailingComments, @@ -808,6 +811,10 @@ export function transformTypeScript(context: TransformationContext): Transformer // TypeScript enum declarations do not exist in ES6 and must be rewritten. return visitEnumDeclaration(node as EnumDeclaration); + case SyntaxKind.EnumLiteralExpression: + // EnumLiteralExpression is rewritten as an ObjectLiteralExpression + return visitEnumLiteralExpression(node as EnumLiteralExpression); + case SyntaxKind.VariableStatement: // TypeScript namespace exports for variable statements must be transformed. return visitVariableStatement(node as VariableStatement); @@ -1783,7 +1790,7 @@ export function transformTypeScript(context: TransformationContext): Transformer * * @param node The enum declaration node. */ - function shouldEmitEnumDeclaration(node: EnumDeclaration) { + function shouldEmitEnumDeclaration(node: EnumDeclaration | EnumLiteralExpression) { return !isEnumConst(node) || shouldPreserveConstEnums(compilerOptions); } @@ -1884,7 +1891,7 @@ export function transformTypeScript(context: TransformationContext): Transformer * * @param node The enum declaration node. */ - function transformEnumBody(node: EnumDeclaration, localName: Identifier): Block { + function transformEnumBody(node: EnumDeclaration | EnumLiteralExpression, localName: Identifier): Block { const savedCurrentNamespaceLocalName = currentNamespaceContainerName; currentNamespaceContainerName = localName; @@ -1940,6 +1947,23 @@ export function transformTypeScript(context: TransformationContext): Transformer ); } + function visitEnumLiteralExpression(node: EnumLiteralExpression): ObjectLiteralExpression { + const properties: readonly ObjectLiteralElementLike[] = node.members.map((member: EnumMember): ObjectLiteralElementLike => { + return member.original as ObjectLiteralElementLike; + }); + + // return factory.updateEnumLiteralExpression( + // node, + // node.modifiers, + // node.name, + // visitNodes(node.members, getObjectLiteralElementVisitor(node), isObjectLiteralElementLike), + // ); + const objectLiteral = factory.createObjectLiteralExpression(properties, /*multiLine*/ true); + setOriginalNode(objectLiteral, node); + setParent(objectLiteral, node.parent); + return objectLiteral; + } + /** * Transforms the value of an enum member. * diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f6ca75a68e17d..379d95d096eea 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -305,6 +305,7 @@ export const enum SyntaxKind { MetaProperty, SyntheticExpression, SatisfiesExpression, + EnumLiteralExpression, // Misc TemplateSpan, @@ -1101,6 +1102,7 @@ export type HasChildren = | BindingElement | ArrayLiteralExpression | ObjectLiteralExpression + | EnumLiteralExpression | PropertyAccessExpression | ElementAccessExpression | CallExpression @@ -1240,6 +1242,7 @@ export type HasJSDoc = | NamedTupleMember | NamespaceExportDeclaration | ObjectLiteralExpression + | EnumLiteralExpression | ParameterDeclaration | ParenthesizedExpression | PropertyAccessExpression @@ -1376,6 +1379,7 @@ export type HasModifiers = | InterfaceDeclaration | TypeAliasDeclaration | EnumDeclaration + | EnumLiteralExpression | ModuleDeclaration | ImportEqualsDeclaration | ImportDeclaration @@ -1412,6 +1416,7 @@ export type IsContainer = | ClassDeclaration | EnumDeclaration | ObjectLiteralExpression + | EnumLiteralExpression | TypeLiteralNode | JSDocTypeLiteral | JsxAttributes @@ -3593,7 +3598,7 @@ export interface TypeAliasDeclaration extends DeclarationStatement, JSDocContain export interface EnumMember extends NamedDeclaration, JSDocContainer { readonly kind: SyntaxKind.EnumMember; - readonly parent: EnumDeclaration; + readonly parent: EnumDeclarationType; // This does include ComputedPropertyName, but the parser will give an error // if it parses a ComputedPropertyName in an EnumMember readonly name: PropertyName; @@ -3607,6 +3612,16 @@ export interface EnumDeclaration extends DeclarationStatement, JSDocContainer { readonly members: NodeArray; } +export interface EnumLiteralExpression extends PrimaryExpression, Declaration, JSDocContainer { + readonly kind: SyntaxKind.EnumLiteralExpression; + readonly parent: VariableDeclaration; + readonly modifiers?: NodeArray; + readonly name: Identifier; // For compatibility with EnumDeclaration -- however this must be equal to the BindingName in the VariableDeclaration. + readonly members: NodeArray; +} + +export type EnumDeclarationType = EnumDeclaration | EnumLiteralExpression; + export type ModuleName = | Identifier | StringLiteral; @@ -4513,6 +4528,7 @@ export interface JsonMinusNumericLiteral extends PrefixUnaryExpression { export type JsonObjectExpression = | ObjectLiteralExpression + | EnumLiteralExpression | ArrayLiteralExpression | JsonMinusNumericLiteral | NumericLiteral @@ -9009,6 +9025,8 @@ export interface NodeFactory { updateTypeAliasDeclaration(node: TypeAliasDeclaration, modifiers: readonly ModifierLike[] | undefined, name: Identifier, typeParameters: readonly TypeParameterDeclaration[] | undefined, type: TypeNode): TypeAliasDeclaration; createEnumDeclaration(modifiers: readonly ModifierLike[] | undefined, name: string | Identifier, members: readonly EnumMember[]): EnumDeclaration; updateEnumDeclaration(node: EnumDeclaration, modifiers: readonly ModifierLike[] | undefined, name: Identifier, members: readonly EnumMember[]): EnumDeclaration; + createEnumLiteralExpression(modifiers: readonly ModifierLike[] | undefined, name: string | Identifier, members: readonly EnumMember[]): EnumLiteralExpression; + updateEnumLiteralExpression(node: EnumLiteralExpression, modifiers: readonly ModifierLike[] | undefined, name: Identifier, members: readonly EnumMember[]): EnumLiteralExpression; createModuleDeclaration(modifiers: readonly ModifierLike[] | undefined, name: ModuleName, body: ModuleBody | undefined, flags?: NodeFlags): ModuleDeclaration; updateModuleDeclaration(node: ModuleDeclaration, modifiers: readonly ModifierLike[] | undefined, name: ModuleName, body: ModuleBody | undefined): ModuleDeclaration; createModuleBlock(statements: readonly Statement[]): ModuleBlock; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 5755369134d8a..3877687ff545d 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -118,6 +118,7 @@ import { EntityNameExpression, EntityNameOrEntityNameExpression, EnumDeclaration, + EnumDeclarationType, EqualityComparer, equalOwnProperties, EqualsToken, @@ -275,7 +276,10 @@ import { isDecorator, isElementAccessExpression, isEnumDeclaration, + isEnumLiteralDeclaration, + isEnumLiteralExpression, isEnumMember, + isEnumTypeReference, isExportAssignment, isExportDeclaration, isExpressionStatement, @@ -2583,7 +2587,7 @@ export function isJsonSourceFile(file: SourceFile): file is JsonSourceFile { } /** @internal */ -export function isEnumConst(node: EnumDeclaration): boolean { +export function isEnumConst(node: EnumDeclarationType): boolean { return !!(getCombinedModifierFlags(node) & ModifierFlags.Const); } @@ -5887,6 +5891,7 @@ export function getOperatorPrecedence(nodeKind: SyntaxKind, operatorKind: Syntax case SyntaxKind.StringLiteral: case SyntaxKind.ArrayLiteralExpression: case SyntaxKind.ObjectLiteralExpression: + case SyntaxKind.EnumLiteralExpression: case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: case SyntaxKind.ClassExpression: @@ -6804,6 +6809,7 @@ export function getAllAccessorDeclarations(declarations: readonly Declaration[] export function getEffectiveTypeAnnotationNode(node: Node): TypeNode | undefined { if (!isInJSFile(node) && isFunctionDeclaration(node)) return undefined; if (isTypeAliasDeclaration(node)) return undefined; // has a .type, is not a type annotation + if (isEnumLiteralDeclaration(node)) return undefined;// has a type reference, but is not used as a hint about the meaning of the initializer. const type = (node as HasType).type; if (type || !isInJSFile(node)) return type; return isJSDocPropertyLikeTag(node) ? node.typeExpression && node.typeExpression.type : getJSDocType(node); diff --git a/src/compiler/utilitiesPublic.ts b/src/compiler/utilitiesPublic.ts index 61bf000c0069a..0e7bc175614cc 100644 --- a/src/compiler/utilitiesPublic.ts +++ b/src/compiler/utilitiesPublic.ts @@ -123,6 +123,7 @@ import { isClassStaticBlockDeclaration, isDecorator, isElementAccessExpression, + isEnumLiteralExpression, isExpandoPropertyDeclaration, isExportAssignment, isExportDeclaration, @@ -1430,6 +1431,14 @@ export function isEnumTypeReference(node: Node): boolean { return isTypeReferenceNode(node) && isIdentifier(node.typeName) && node.typeName.escapedText === "enum" && !node.typeArguments; } +export function isEnumLiteralDeclaration(node: Node): boolean { + return isVariableDeclaration(node) && hasType(node) && isEnumTypeReference(node.type!) && hasInitializer(node) && isEnumLiteralExpression(node.initializer!); +} + +export function isEnumTypeAnnotation(node: Node): boolean { + if (isIdentifier(node)) node = node.parent; + return isEnumTypeReference(node) && node.parent && isEnumLiteralDeclaration(node.parent); +} export function skipPartiallyEmittedExpressions(node: Expression): Expression; export function skipPartiallyEmittedExpressions(node: Node): Node; @@ -2011,6 +2020,7 @@ function isLeftHandSideExpressionKind(kind: SyntaxKind): boolean { case SyntaxKind.ArrayLiteralExpression: case SyntaxKind.ParenthesizedExpression: case SyntaxKind.ObjectLiteralExpression: + case SyntaxKind.EnumLiteralExpression: case SyntaxKind.ClassExpression: case SyntaxKind.FunctionExpression: case SyntaxKind.Identifier: @@ -2328,6 +2338,7 @@ function isDeclarationKind(kind: SyntaxKind) { || kind === SyntaxKind.ClassStaticBlockDeclaration || kind === SyntaxKind.Constructor || kind === SyntaxKind.EnumDeclaration + || kind === SyntaxKind.EnumLiteralExpression || kind === SyntaxKind.EnumMember || kind === SyntaxKind.ExportSpecifier || kind === SyntaxKind.FunctionDeclaration diff --git a/src/compiler/visitorPublic.ts b/src/compiler/visitorPublic.ts index dbd49379e5750..7dc82c1518b02 100644 --- a/src/compiler/visitorPublic.ts +++ b/src/compiler/visitorPublic.ts @@ -1480,6 +1480,16 @@ const visitEachChildTable: VisitEachChildTable = { ); }, + [SyntaxKind.EnumLiteralExpression]: function visitEachChildOfEnumDeclaration(node, visitor, context, nodesVisitor, nodeVisitor, _tokenVisitor) { + return context.factory.updateEnumLiteralExpression( + node, + nodesVisitor(node.modifiers, visitor, isModifierLike), + Debug.checkDefined(nodeVisitor(node.name, visitor, isIdentifier)), + nodesVisitor(node.members, visitor, isEnumMember), + ); + }, + + [SyntaxKind.ModuleDeclaration]: function visitEachChildOfModuleDeclaration(node, visitor, context, nodesVisitor, nodeVisitor, _tokenVisitor) { return context.factory.updateModuleDeclaration( node, From 335334c53f72a9368537a125ebf63e2a2a507ca2 Mon Sep 17 00:00:00 2001 From: Caitlin Potter Date: Thu, 13 Mar 2025 10:50:22 -0400 Subject: [PATCH 04/14] Fix issues reporting first enum member not having an initializer --- src/compiler/checker.ts | 4 ++-- tests/baselines/reference/asEnumBasics.errors.txt | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2b2d741b3b15d..4bb242c3d5e5b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -47051,7 +47051,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { nodeLinks.flags |= NodeCheckFlags.EnumValuesComputed; // EnumLiteralExpressions are essentially plain ObjectLiteralExpressions and can not have computed values. const hasComputedValues = !isEnumLiteralExpression(node); - let autoValue: number | undefined = hasComputedValues ? undefined : 0; + let autoValue: number | undefined = hasComputedValues ? 0 : undefined; let previous: EnumMember | undefined; for (const member of node.members) { const result = computeEnumMemberValue(member, autoValue, previous); @@ -47237,7 +47237,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const enumIsConst = isEnumConst(node); // check that const is placed\omitted on all enum declarations forEach(enumSymbol.declarations, decl => { - if (isEnumDeclaration(decl) && isEnumConst(decl) !== enumIsConst) { + if ((isEnumDeclaration(decl) || isEnumLiteralExpression(decl)) && isEnumConst(decl) !== enumIsConst) { error(getNameOfDeclaration(decl), Diagnostics.Enum_declarations_must_all_be_const_or_non_const); } }); diff --git a/tests/baselines/reference/asEnumBasics.errors.txt b/tests/baselines/reference/asEnumBasics.errors.txt index ca6002f9d48f5..6bf373d611113 100644 --- a/tests/baselines/reference/asEnumBasics.errors.txt +++ b/tests/baselines/reference/asEnumBasics.errors.txt @@ -1,3 +1,4 @@ +asEnumBasics.ts(6,5): error TS1061: Enum member must have initializer. asEnumBasics.ts(7,5): error TS1061: Enum member must have initializer. asEnumBasics.ts(21,7): error TS2322: Type '"exists"' is not assignable to type 'E1'. asEnumBasics.ts(23,7): error TS2322: Type '"string"' is not assignable to type 'E1'. @@ -5,13 +6,15 @@ asEnumBasics.ts(33,33): error TS2345: Argument of type '"exists"' is not assigna asEnumBasics.ts(38,32): error TS2345: Argument of type '"string"' is not assignable to parameter of type 'E1'. -==== asEnumBasics.ts (5 errors) ==== +==== asEnumBasics.ts (6 errors) ==== // Enum without initializers have first member = 0 and successive members = N + 1 // `as enum` does not introduce auto-incrementing behaviour. let ExistingShorthand = "exists"; const E1: enum = { NonexistingShorthand, // error -- EnumLiteralExpressions require explicit property definitions. + ~~~~~~~~~~~~~~~~~~~~ +!!! error TS1061: Enum member must have initializer. ExistingShorthand, // error -- EnumLiteralExpressions require explicit property definitions. ~~~~~~~~~~~~~~~~~ !!! error TS1061: Enum member must have initializer. From 2a721678e646790307576fa5d6638288ff56dcd3 Mon Sep 17 00:00:00 2001 From: Caitlin Potter Date: Thu, 13 Mar 2025 10:52:37 -0400 Subject: [PATCH 05/14] nit: remove reference to syntax (TODO: rename the test files to refer to Enum Literals) --- tests/baselines/reference/asEnumBasics.errors.txt | 2 +- tests/baselines/reference/asEnumBasics.js | 14 +++++++------- tests/baselines/reference/asEnumBasics.symbols | 2 +- tests/baselines/reference/asEnumBasics.types | 2 +- tests/cases/conformance/asEnum/asEnumBasics.ts | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/baselines/reference/asEnumBasics.errors.txt b/tests/baselines/reference/asEnumBasics.errors.txt index 6bf373d611113..12726f88dff1a 100644 --- a/tests/baselines/reference/asEnumBasics.errors.txt +++ b/tests/baselines/reference/asEnumBasics.errors.txt @@ -9,7 +9,7 @@ asEnumBasics.ts(38,32): error TS2345: Argument of type '"string"' is not assigna ==== asEnumBasics.ts (6 errors) ==== // Enum without initializers have first member = 0 and successive members = N + 1 - // `as enum` does not introduce auto-incrementing behaviour. + // Enum literal syntax does not implement auto-incrementing behaviour. let ExistingShorthand = "exists"; const E1: enum = { NonexistingShorthand, // error -- EnumLiteralExpressions require explicit property definitions. diff --git a/tests/baselines/reference/asEnumBasics.js b/tests/baselines/reference/asEnumBasics.js index 17a3d250f5d9e..712c6bba4960b 100644 --- a/tests/baselines/reference/asEnumBasics.js +++ b/tests/baselines/reference/asEnumBasics.js @@ -3,7 +3,7 @@ //// [asEnumBasics.ts] // Enum without initializers have first member = 0 and successive members = N + 1 -// `as enum` does not introduce auto-incrementing behaviour. +// Enum literal syntax does not implement auto-incrementing behaviour. let ExistingShorthand = "exists"; const E1: enum = { NonexistingShorthand, // error -- EnumLiteralExpressions require explicit property definitions. @@ -43,23 +43,23 @@ const sval_bad2: E1 = functest("string"); // error //// [asEnumBasics.js] // Enum without initializers have first member = 0 and successive members = N + 1 -// `as enum` does not introduce auto-incrementing behaviour. +// Enum literal syntax does not implement auto-incrementing behaviour. var ExistingShorthand = "exists"; var E1 = { NonexistingShorthand: NonexistingShorthand// Enum without initializers have first member = 0 and successive members = N + 1 - // `as enum` does not introduce auto-incrementing behaviour. + // Enum literal syntax does not implement auto-incrementing behaviour. , // Enum without initializers have first member = 0 and successive members = N + 1 ExistingShorthand: ExistingShorthand// Enum without initializers have first member = 0 and successive members = N + 1 - // `as enum` does not introduce auto-incrementing behaviour. + // Enum literal syntax does not implement auto-incrementing behaviour. , // Enum without initializers have first member = 0 and successive members = N + 1 Int: 1// Enum without initializers have first member = 0 and successive members = N + 1 - // `as enum` does not introduce auto-incrementing behaviour. + // Enum literal syntax does not implement auto-incrementing behaviour. , // Enum without initializers have first member = 0 and successive members = N + 1 String: "string"// Enum without initializers have first member = 0 and successive members = N + 1 - // `as enum` does not introduce auto-incrementing behaviour. + // Enum literal syntax does not implement auto-incrementing behaviour. , // Enum without initializers have first member = 0 and successive members = N + 1 Flag: 8// Enum without initializers have first member = 0 and successive members = N + 1 - // `as enum` does not introduce auto-incrementing behaviour. + // Enum literal syntax does not implement auto-incrementing behaviour. }; // Valid assignments var nonexist = E1.NonexistingShorthand; // ok diff --git a/tests/baselines/reference/asEnumBasics.symbols b/tests/baselines/reference/asEnumBasics.symbols index f369909c193db..5abe9a5f735c0 100644 --- a/tests/baselines/reference/asEnumBasics.symbols +++ b/tests/baselines/reference/asEnumBasics.symbols @@ -3,7 +3,7 @@ === asEnumBasics.ts === // Enum without initializers have first member = 0 and successive members = N + 1 -// `as enum` does not introduce auto-incrementing behaviour. +// Enum literal syntax does not implement auto-incrementing behaviour. let ExistingShorthand = "exists"; >ExistingShorthand : Symbol(ExistingShorthand, Decl(asEnumBasics.ts, 3, 3)) diff --git a/tests/baselines/reference/asEnumBasics.types b/tests/baselines/reference/asEnumBasics.types index ab94aba567ac2..5e62152cbd3de 100644 --- a/tests/baselines/reference/asEnumBasics.types +++ b/tests/baselines/reference/asEnumBasics.types @@ -3,7 +3,7 @@ === asEnumBasics.ts === // Enum without initializers have first member = 0 and successive members = N + 1 -// `as enum` does not introduce auto-incrementing behaviour. +// Enum literal syntax does not implement auto-incrementing behaviour. let ExistingShorthand = "exists"; >ExistingShorthand : string > : ^^^^^^ diff --git a/tests/cases/conformance/asEnum/asEnumBasics.ts b/tests/cases/conformance/asEnum/asEnumBasics.ts index e0f624cf0f560..6bdcd19f2cf17 100644 --- a/tests/cases/conformance/asEnum/asEnumBasics.ts +++ b/tests/cases/conformance/asEnum/asEnumBasics.ts @@ -1,6 +1,6 @@ // Enum without initializers have first member = 0 and successive members = N + 1 -// `as enum` does not introduce auto-incrementing behaviour. +// Enum literal syntax does not implement auto-incrementing behaviour. let ExistingShorthand = "exists"; const E1: enum = { NonexistingShorthand, // error -- EnumLiteralExpressions require explicit property definitions. From 52aec09e4c2a4eac690e7d308b44e3259c85c590 Mon Sep 17 00:00:00 2001 From: Caitlin Potter Date: Thu, 13 Mar 2025 10:57:59 -0400 Subject: [PATCH 06/14] rename asEnum* to enumLiteral* --- .../baselines/reference/asEnumBasics.symbols | 141 ------------------ tests/baselines/reference/asEnumSimple.js | 16 -- .../baselines/reference/asEnumSimple.symbols | 19 --- ...rrors.txt => enumLiteralBasics.errors.txt} | 14 +- .../{asEnumBasics.js => enumLiteralBasics.js} | 6 +- .../reference/enumLiteralBasics.symbols | 141 ++++++++++++++++++ ...umBasics.types => enumLiteralBasics.types} | 4 +- .../baselines/reference/enumLiteralSimple.js | 16 ++ .../reference/enumLiteralSimple.symbols | 19 +++ ...umSimple.types => enumLiteralSimple.types} | 4 +- .../enumLiteralBasics.ts} | 0 .../enumLiteralSimple.ts} | 0 12 files changed, 190 insertions(+), 190 deletions(-) delete mode 100644 tests/baselines/reference/asEnumBasics.symbols delete mode 100644 tests/baselines/reference/asEnumSimple.js delete mode 100644 tests/baselines/reference/asEnumSimple.symbols rename tests/baselines/reference/{asEnumBasics.errors.txt => enumLiteralBasics.errors.txt} (77%) rename tests/baselines/reference/{asEnumBasics.js => enumLiteralBasics.js} (96%) create mode 100644 tests/baselines/reference/enumLiteralBasics.symbols rename tests/baselines/reference/{asEnumBasics.types => enumLiteralBasics.types} (94%) create mode 100644 tests/baselines/reference/enumLiteralSimple.js create mode 100644 tests/baselines/reference/enumLiteralSimple.symbols rename tests/baselines/reference/{asEnumSimple.types => enumLiteralSimple.types} (65%) rename tests/cases/conformance/{asEnum/asEnumBasics.ts => enums/enumLiteralBasics.ts} (100%) rename tests/cases/conformance/{asEnum/asEnumSimple.ts => enums/enumLiteralSimple.ts} (100%) diff --git a/tests/baselines/reference/asEnumBasics.symbols b/tests/baselines/reference/asEnumBasics.symbols deleted file mode 100644 index 5abe9a5f735c0..0000000000000 --- a/tests/baselines/reference/asEnumBasics.symbols +++ /dev/null @@ -1,141 +0,0 @@ -//// [tests/cases/conformance/asEnum/asEnumBasics.ts] //// - -=== asEnumBasics.ts === -// Enum without initializers have first member = 0 and successive members = N + 1 - -// Enum literal syntax does not implement auto-incrementing behaviour. -let ExistingShorthand = "exists"; ->ExistingShorthand : Symbol(ExistingShorthand, Decl(asEnumBasics.ts, 3, 3)) - -const E1: enum = { ->E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) ->E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) - - NonexistingShorthand, // error -- EnumLiteralExpressions require explicit property definitions. ->NonexistingShorthand : Symbol(E1.NonexistingShorthand, Decl(asEnumBasics.ts, 4, 18)) - - ExistingShorthand, // error -- EnumLiteralExpressions require explicit property definitions. ->ExistingShorthand : Symbol(E1.ExistingShorthand, Decl(asEnumBasics.ts, 5, 25)) - - Int: 1, // ok ->Int : Symbol(E1.Int, Decl(asEnumBasics.ts, 6, 22)) - - String: "string", // ok ->String : Symbol(E1.String, Decl(asEnumBasics.ts, 7, 11)) - - Flag: 8, // ok ->Flag : Symbol(E1.Flag, Decl(asEnumBasics.ts, 8, 21)) - -}; - -// Valid assignments -const nonexist: E1 = E1.NonexistingShorthand; // ok ->nonexist : Symbol(nonexist, Decl(asEnumBasics.ts, 13, 5)) ->E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) ->E1.NonexistingShorthand : Symbol(E1.NonexistingShorthand, Decl(asEnumBasics.ts, 4, 18)) ->E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) ->NonexistingShorthand : Symbol(E1.NonexistingShorthand, Decl(asEnumBasics.ts, 4, 18)) - -const exist: E1 = E1.ExistingShorthand; // ok ->exist : Symbol(exist, Decl(asEnumBasics.ts, 14, 5)) ->E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) ->E1.ExistingShorthand : Symbol(E1.ExistingShorthand, Decl(asEnumBasics.ts, 5, 25)) ->E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) ->ExistingShorthand : Symbol(E1.ExistingShorthand, Decl(asEnumBasics.ts, 5, 25)) - -const ival: E1 = E1.Int; // ok ->ival : Symbol(ival, Decl(asEnumBasics.ts, 15, 5)) ->E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) ->E1.Int : Symbol(E1.Int, Decl(asEnumBasics.ts, 6, 22)) ->E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) ->Int : Symbol(E1.Int, Decl(asEnumBasics.ts, 6, 22)) - -const sval: E1 = E1.String; // ok ->sval : Symbol(sval, Decl(asEnumBasics.ts, 16, 5)) ->E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) ->E1.String : Symbol(E1.String, Decl(asEnumBasics.ts, 7, 11)) ->E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) ->String : Symbol(E1.String, Decl(asEnumBasics.ts, 7, 11)) - -// Assigning values which are not part of the enum despite being present in the enum -const nonexist_bad: E1 = undefined; // error ->nonexist_bad : Symbol(nonexist_bad, Decl(asEnumBasics.ts, 19, 5)) ->E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) ->undefined : Symbol(undefined) - -const exist_bad: E1 = "exists"; // error ->exist_bad : Symbol(exist_bad, Decl(asEnumBasics.ts, 20, 5)) ->E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) - -const ival_good: E1 = 1; // ok -- TypeScript is permissive of this in enums, to permit things like bitwise combinations of enum values. ->ival_good : Symbol(ival_good, Decl(asEnumBasics.ts, 21, 5)) ->E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) - -const sval_bad: E1 = "string"; // error ->sval_bad : Symbol(sval_bad, Decl(asEnumBasics.ts, 22, 5)) ->E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) - -const ival_notpresent: E1 = 4; // ok -- TypeScript is permissive of this in enums, to permit things like bitwise combinations of enum values. ->ival_notpresent : Symbol(ival_notpresent, Decl(asEnumBasics.ts, 24, 5)) ->E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) - -function functest(value: E1) { ->functest : Symbol(functest, Decl(asEnumBasics.ts, 24, 30)) ->value : Symbol(value, Decl(asEnumBasics.ts, 26, 18)) ->E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) - - console.log(value); ->console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) ->console : Symbol(console, Decl(lib.dom.d.ts, --, --)) ->log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) ->value : Symbol(value, Decl(asEnumBasics.ts, 26, 18)) - - return value; ->value : Symbol(value, Decl(asEnumBasics.ts, 26, 18)) -} - -const nonexist_bad2: E1 = functest(undefined); // error ->nonexist_bad2 : Symbol(nonexist_bad2, Decl(asEnumBasics.ts, 31, 5)) ->E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) ->functest : Symbol(functest, Decl(asEnumBasics.ts, 24, 30)) ->undefined : Symbol(undefined) - -const exist_bad2: E1 = functest("exists"); // error ->exist_bad2 : Symbol(exist_bad2, Decl(asEnumBasics.ts, 32, 5)) ->E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) ->functest : Symbol(functest, Decl(asEnumBasics.ts, 24, 30)) - -const ival_good2: E1 = functest(1); // ok ->ival_good2 : Symbol(ival_good2, Decl(asEnumBasics.ts, 33, 5)) ->E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) ->functest : Symbol(functest, Decl(asEnumBasics.ts, 24, 30)) - -const ival_good3: E1 = functest(4); // ok ->ival_good3 : Symbol(ival_good3, Decl(asEnumBasics.ts, 34, 5)) ->E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) ->functest : Symbol(functest, Decl(asEnumBasics.ts, 24, 30)) - -const ival_good4: E1 = functest(E1.Int | E1.Flag); // ok ->ival_good4 : Symbol(ival_good4, Decl(asEnumBasics.ts, 35, 5)) ->E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) ->functest : Symbol(functest, Decl(asEnumBasics.ts, 24, 30)) ->E1.Int : Symbol(E1.Int, Decl(asEnumBasics.ts, 6, 22)) ->E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) ->Int : Symbol(E1.Int, Decl(asEnumBasics.ts, 6, 22)) ->E1.Flag : Symbol(E1.Flag, Decl(asEnumBasics.ts, 8, 21)) ->E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) ->Flag : Symbol(E1.Flag, Decl(asEnumBasics.ts, 8, 21)) - -const sval_good2: E1 = functest(E1.String); ->sval_good2 : Symbol(sval_good2, Decl(asEnumBasics.ts, 36, 5)) ->E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) ->functest : Symbol(functest, Decl(asEnumBasics.ts, 24, 30)) ->E1.String : Symbol(E1.String, Decl(asEnumBasics.ts, 7, 11)) ->E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) ->String : Symbol(E1.String, Decl(asEnumBasics.ts, 7, 11)) - -const sval_bad2: E1 = functest("string"); // error ->sval_bad2 : Symbol(sval_bad2, Decl(asEnumBasics.ts, 37, 5)) ->E1 : Symbol(E1, Decl(asEnumBasics.ts, 4, 5), Decl(asEnumBasics.ts, 4, 5)) ->functest : Symbol(functest, Decl(asEnumBasics.ts, 24, 30)) - diff --git a/tests/baselines/reference/asEnumSimple.js b/tests/baselines/reference/asEnumSimple.js deleted file mode 100644 index 64d170cd77c28..0000000000000 --- a/tests/baselines/reference/asEnumSimple.js +++ /dev/null @@ -1,16 +0,0 @@ -//// [tests/cases/conformance/asEnum/asEnumSimple.ts] //// - -//// [asEnumSimple.ts] -const ENUM: enum = { - a: 1, - b: 2 -}; - -const a: ENUM = 1 - -//// [asEnumSimple.js] -var ENUM = { - a: 1, - b: 2 -}; -var a = 1; diff --git a/tests/baselines/reference/asEnumSimple.symbols b/tests/baselines/reference/asEnumSimple.symbols deleted file mode 100644 index 89db315f5f67f..0000000000000 --- a/tests/baselines/reference/asEnumSimple.symbols +++ /dev/null @@ -1,19 +0,0 @@ -//// [tests/cases/conformance/asEnum/asEnumSimple.ts] //// - -=== asEnumSimple.ts === -const ENUM: enum = { ->ENUM : Symbol(ENUM, Decl(asEnumSimple.ts, 0, 5), Decl(asEnumSimple.ts, 0, 5)) ->ENUM : Symbol(ENUM, Decl(asEnumSimple.ts, 0, 5), Decl(asEnumSimple.ts, 0, 5)) - - a: 1, ->a : Symbol(ENUM.a, Decl(asEnumSimple.ts, 0, 20)) - - b: 2 ->b : Symbol(ENUM.b, Decl(asEnumSimple.ts, 1, 7)) - -}; - -const a: ENUM = 1 ->a : Symbol(a, Decl(asEnumSimple.ts, 5, 5)) ->ENUM : Symbol(ENUM, Decl(asEnumSimple.ts, 0, 5), Decl(asEnumSimple.ts, 0, 5)) - diff --git a/tests/baselines/reference/asEnumBasics.errors.txt b/tests/baselines/reference/enumLiteralBasics.errors.txt similarity index 77% rename from tests/baselines/reference/asEnumBasics.errors.txt rename to tests/baselines/reference/enumLiteralBasics.errors.txt index 12726f88dff1a..fed2f2741a8b1 100644 --- a/tests/baselines/reference/asEnumBasics.errors.txt +++ b/tests/baselines/reference/enumLiteralBasics.errors.txt @@ -1,12 +1,12 @@ -asEnumBasics.ts(6,5): error TS1061: Enum member must have initializer. -asEnumBasics.ts(7,5): error TS1061: Enum member must have initializer. -asEnumBasics.ts(21,7): error TS2322: Type '"exists"' is not assignable to type 'E1'. -asEnumBasics.ts(23,7): error TS2322: Type '"string"' is not assignable to type 'E1'. -asEnumBasics.ts(33,33): error TS2345: Argument of type '"exists"' is not assignable to parameter of type 'E1'. -asEnumBasics.ts(38,32): error TS2345: Argument of type '"string"' is not assignable to parameter of type 'E1'. +enumLiteralBasics.ts(6,5): error TS1061: Enum member must have initializer. +enumLiteralBasics.ts(7,5): error TS1061: Enum member must have initializer. +enumLiteralBasics.ts(21,7): error TS2322: Type '"exists"' is not assignable to type 'E1'. +enumLiteralBasics.ts(23,7): error TS2322: Type '"string"' is not assignable to type 'E1'. +enumLiteralBasics.ts(33,33): error TS2345: Argument of type '"exists"' is not assignable to parameter of type 'E1'. +enumLiteralBasics.ts(38,32): error TS2345: Argument of type '"string"' is not assignable to parameter of type 'E1'. -==== asEnumBasics.ts (6 errors) ==== +==== enumLiteralBasics.ts (6 errors) ==== // Enum without initializers have first member = 0 and successive members = N + 1 // Enum literal syntax does not implement auto-incrementing behaviour. diff --git a/tests/baselines/reference/asEnumBasics.js b/tests/baselines/reference/enumLiteralBasics.js similarity index 96% rename from tests/baselines/reference/asEnumBasics.js rename to tests/baselines/reference/enumLiteralBasics.js index 712c6bba4960b..a1671ef56873f 100644 --- a/tests/baselines/reference/asEnumBasics.js +++ b/tests/baselines/reference/enumLiteralBasics.js @@ -1,6 +1,6 @@ -//// [tests/cases/conformance/asEnum/asEnumBasics.ts] //// +//// [tests/cases/conformance/enums/enumLiteralBasics.ts] //// -//// [asEnumBasics.ts] +//// [enumLiteralBasics.ts] // Enum without initializers have first member = 0 and successive members = N + 1 // Enum literal syntax does not implement auto-incrementing behaviour. @@ -41,7 +41,7 @@ const sval_good2: E1 = functest(E1.String); const sval_bad2: E1 = functest("string"); // error -//// [asEnumBasics.js] +//// [enumLiteralBasics.js] // Enum without initializers have first member = 0 and successive members = N + 1 // Enum literal syntax does not implement auto-incrementing behaviour. var ExistingShorthand = "exists"; diff --git a/tests/baselines/reference/enumLiteralBasics.symbols b/tests/baselines/reference/enumLiteralBasics.symbols new file mode 100644 index 0000000000000..966e00ece36f0 --- /dev/null +++ b/tests/baselines/reference/enumLiteralBasics.symbols @@ -0,0 +1,141 @@ +//// [tests/cases/conformance/enums/enumLiteralBasics.ts] //// + +=== enumLiteralBasics.ts === +// Enum without initializers have first member = 0 and successive members = N + 1 + +// Enum literal syntax does not implement auto-incrementing behaviour. +let ExistingShorthand = "exists"; +>ExistingShorthand : Symbol(ExistingShorthand, Decl(enumLiteralBasics.ts, 3, 3)) + +const E1: enum = { +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) + + NonexistingShorthand, // error -- EnumLiteralExpressions require explicit property definitions. +>NonexistingShorthand : Symbol(E1.NonexistingShorthand, Decl(enumLiteralBasics.ts, 4, 18)) + + ExistingShorthand, // error -- EnumLiteralExpressions require explicit property definitions. +>ExistingShorthand : Symbol(E1.ExistingShorthand, Decl(enumLiteralBasics.ts, 5, 25)) + + Int: 1, // ok +>Int : Symbol(E1.Int, Decl(enumLiteralBasics.ts, 6, 22)) + + String: "string", // ok +>String : Symbol(E1.String, Decl(enumLiteralBasics.ts, 7, 11)) + + Flag: 8, // ok +>Flag : Symbol(E1.Flag, Decl(enumLiteralBasics.ts, 8, 21)) + +}; + +// Valid assignments +const nonexist: E1 = E1.NonexistingShorthand; // ok +>nonexist : Symbol(nonexist, Decl(enumLiteralBasics.ts, 13, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) +>E1.NonexistingShorthand : Symbol(E1.NonexistingShorthand, Decl(enumLiteralBasics.ts, 4, 18)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) +>NonexistingShorthand : Symbol(E1.NonexistingShorthand, Decl(enumLiteralBasics.ts, 4, 18)) + +const exist: E1 = E1.ExistingShorthand; // ok +>exist : Symbol(exist, Decl(enumLiteralBasics.ts, 14, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) +>E1.ExistingShorthand : Symbol(E1.ExistingShorthand, Decl(enumLiteralBasics.ts, 5, 25)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) +>ExistingShorthand : Symbol(E1.ExistingShorthand, Decl(enumLiteralBasics.ts, 5, 25)) + +const ival: E1 = E1.Int; // ok +>ival : Symbol(ival, Decl(enumLiteralBasics.ts, 15, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) +>E1.Int : Symbol(E1.Int, Decl(enumLiteralBasics.ts, 6, 22)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) +>Int : Symbol(E1.Int, Decl(enumLiteralBasics.ts, 6, 22)) + +const sval: E1 = E1.String; // ok +>sval : Symbol(sval, Decl(enumLiteralBasics.ts, 16, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) +>E1.String : Symbol(E1.String, Decl(enumLiteralBasics.ts, 7, 11)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) +>String : Symbol(E1.String, Decl(enumLiteralBasics.ts, 7, 11)) + +// Assigning values which are not part of the enum despite being present in the enum +const nonexist_bad: E1 = undefined; // error +>nonexist_bad : Symbol(nonexist_bad, Decl(enumLiteralBasics.ts, 19, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) +>undefined : Symbol(undefined) + +const exist_bad: E1 = "exists"; // error +>exist_bad : Symbol(exist_bad, Decl(enumLiteralBasics.ts, 20, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) + +const ival_good: E1 = 1; // ok -- TypeScript is permissive of this in enums, to permit things like bitwise combinations of enum values. +>ival_good : Symbol(ival_good, Decl(enumLiteralBasics.ts, 21, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) + +const sval_bad: E1 = "string"; // error +>sval_bad : Symbol(sval_bad, Decl(enumLiteralBasics.ts, 22, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) + +const ival_notpresent: E1 = 4; // ok -- TypeScript is permissive of this in enums, to permit things like bitwise combinations of enum values. +>ival_notpresent : Symbol(ival_notpresent, Decl(enumLiteralBasics.ts, 24, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) + +function functest(value: E1) { +>functest : Symbol(functest, Decl(enumLiteralBasics.ts, 24, 30)) +>value : Symbol(value, Decl(enumLiteralBasics.ts, 26, 18)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) + + console.log(value); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>value : Symbol(value, Decl(enumLiteralBasics.ts, 26, 18)) + + return value; +>value : Symbol(value, Decl(enumLiteralBasics.ts, 26, 18)) +} + +const nonexist_bad2: E1 = functest(undefined); // error +>nonexist_bad2 : Symbol(nonexist_bad2, Decl(enumLiteralBasics.ts, 31, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) +>functest : Symbol(functest, Decl(enumLiteralBasics.ts, 24, 30)) +>undefined : Symbol(undefined) + +const exist_bad2: E1 = functest("exists"); // error +>exist_bad2 : Symbol(exist_bad2, Decl(enumLiteralBasics.ts, 32, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) +>functest : Symbol(functest, Decl(enumLiteralBasics.ts, 24, 30)) + +const ival_good2: E1 = functest(1); // ok +>ival_good2 : Symbol(ival_good2, Decl(enumLiteralBasics.ts, 33, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) +>functest : Symbol(functest, Decl(enumLiteralBasics.ts, 24, 30)) + +const ival_good3: E1 = functest(4); // ok +>ival_good3 : Symbol(ival_good3, Decl(enumLiteralBasics.ts, 34, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) +>functest : Symbol(functest, Decl(enumLiteralBasics.ts, 24, 30)) + +const ival_good4: E1 = functest(E1.Int | E1.Flag); // ok +>ival_good4 : Symbol(ival_good4, Decl(enumLiteralBasics.ts, 35, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) +>functest : Symbol(functest, Decl(enumLiteralBasics.ts, 24, 30)) +>E1.Int : Symbol(E1.Int, Decl(enumLiteralBasics.ts, 6, 22)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) +>Int : Symbol(E1.Int, Decl(enumLiteralBasics.ts, 6, 22)) +>E1.Flag : Symbol(E1.Flag, Decl(enumLiteralBasics.ts, 8, 21)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) +>Flag : Symbol(E1.Flag, Decl(enumLiteralBasics.ts, 8, 21)) + +const sval_good2: E1 = functest(E1.String); +>sval_good2 : Symbol(sval_good2, Decl(enumLiteralBasics.ts, 36, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) +>functest : Symbol(functest, Decl(enumLiteralBasics.ts, 24, 30)) +>E1.String : Symbol(E1.String, Decl(enumLiteralBasics.ts, 7, 11)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) +>String : Symbol(E1.String, Decl(enumLiteralBasics.ts, 7, 11)) + +const sval_bad2: E1 = functest("string"); // error +>sval_bad2 : Symbol(sval_bad2, Decl(enumLiteralBasics.ts, 37, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) +>functest : Symbol(functest, Decl(enumLiteralBasics.ts, 24, 30)) + diff --git a/tests/baselines/reference/asEnumBasics.types b/tests/baselines/reference/enumLiteralBasics.types similarity index 94% rename from tests/baselines/reference/asEnumBasics.types rename to tests/baselines/reference/enumLiteralBasics.types index 5e62152cbd3de..8640f5138018b 100644 --- a/tests/baselines/reference/asEnumBasics.types +++ b/tests/baselines/reference/enumLiteralBasics.types @@ -1,6 +1,6 @@ -//// [tests/cases/conformance/asEnum/asEnumBasics.ts] //// +//// [tests/cases/conformance/enums/enumLiteralBasics.ts] //// -=== asEnumBasics.ts === +=== enumLiteralBasics.ts === // Enum without initializers have first member = 0 and successive members = N + 1 // Enum literal syntax does not implement auto-incrementing behaviour. diff --git a/tests/baselines/reference/enumLiteralSimple.js b/tests/baselines/reference/enumLiteralSimple.js new file mode 100644 index 0000000000000..5ddaa384269b5 --- /dev/null +++ b/tests/baselines/reference/enumLiteralSimple.js @@ -0,0 +1,16 @@ +//// [tests/cases/conformance/enums/enumLiteralSimple.ts] //// + +//// [enumLiteralSimple.ts] +const ENUM: enum = { + a: 1, + b: 2 +}; + +const a: ENUM = 1 + +//// [enumLiteralSimple.js] +var ENUM = { + a: 1, + b: 2 +}; +var a = 1; diff --git a/tests/baselines/reference/enumLiteralSimple.symbols b/tests/baselines/reference/enumLiteralSimple.symbols new file mode 100644 index 0000000000000..4784ea41a16dc --- /dev/null +++ b/tests/baselines/reference/enumLiteralSimple.symbols @@ -0,0 +1,19 @@ +//// [tests/cases/conformance/enums/enumLiteralSimple.ts] //// + +=== enumLiteralSimple.ts === +const ENUM: enum = { +>ENUM : Symbol(ENUM, Decl(enumLiteralSimple.ts, 0, 5), Decl(enumLiteralSimple.ts, 0, 5)) +>ENUM : Symbol(ENUM, Decl(enumLiteralSimple.ts, 0, 5), Decl(enumLiteralSimple.ts, 0, 5)) + + a: 1, +>a : Symbol(ENUM.a, Decl(enumLiteralSimple.ts, 0, 20)) + + b: 2 +>b : Symbol(ENUM.b, Decl(enumLiteralSimple.ts, 1, 7)) + +}; + +const a: ENUM = 1 +>a : Symbol(a, Decl(enumLiteralSimple.ts, 5, 5)) +>ENUM : Symbol(ENUM, Decl(enumLiteralSimple.ts, 0, 5), Decl(enumLiteralSimple.ts, 0, 5)) + diff --git a/tests/baselines/reference/asEnumSimple.types b/tests/baselines/reference/enumLiteralSimple.types similarity index 65% rename from tests/baselines/reference/asEnumSimple.types rename to tests/baselines/reference/enumLiteralSimple.types index 21c11261f3cac..b1be509828138 100644 --- a/tests/baselines/reference/asEnumSimple.types +++ b/tests/baselines/reference/enumLiteralSimple.types @@ -1,6 +1,6 @@ -//// [tests/cases/conformance/asEnum/asEnumSimple.ts] //// +//// [tests/cases/conformance/enums/enumLiteralSimple.ts] //// -=== asEnumSimple.ts === +=== enumLiteralSimple.ts === const ENUM: enum = { >ENUM : ENUM > : ^^^^ diff --git a/tests/cases/conformance/asEnum/asEnumBasics.ts b/tests/cases/conformance/enums/enumLiteralBasics.ts similarity index 100% rename from tests/cases/conformance/asEnum/asEnumBasics.ts rename to tests/cases/conformance/enums/enumLiteralBasics.ts diff --git a/tests/cases/conformance/asEnum/asEnumSimple.ts b/tests/cases/conformance/enums/enumLiteralSimple.ts similarity index 100% rename from tests/cases/conformance/asEnum/asEnumSimple.ts rename to tests/cases/conformance/enums/enumLiteralSimple.ts From 3650dd4a04aec64ea4a45849788ffda0bd67660c Mon Sep 17 00:00:00 2001 From: Caitlin Potter Date: Thu, 13 Mar 2025 11:51:52 -0400 Subject: [PATCH 07/14] fix the comments being emitted (removing all comments from each member) --- src/compiler/factory/nodeFactory.ts | 3 ++- src/compiler/parser.ts | 17 ++--------------- src/compiler/transformers/ts.ts | 6 +++++- .../baselines/reference/enumLiteralBasics.js | 19 +++++-------------- 4 files changed, 14 insertions(+), 31 deletions(-) diff --git a/src/compiler/factory/nodeFactory.ts b/src/compiler/factory/nodeFactory.ts index 61e4efb9f5034..757706ac71938 100644 --- a/src/compiler/factory/nodeFactory.ts +++ b/src/compiler/factory/nodeFactory.ts @@ -6043,13 +6043,14 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode // // @api - function createEnumMember(name: string | PropertyName, initializer?: Expression) { + function createEnumMember(name: string | PropertyName, initializer?: Expression, objectAssignmentInitializer?: Expression) { const node = createBaseDeclaration(SyntaxKind.EnumMember); node.name = asName(name); node.initializer = initializer && parenthesizerRules().parenthesizeExpressionForDisallowedComma(initializer); node.transformFlags |= propagateChildFlags(node.name) | propagateChildFlags(node.initializer) | TransformFlags.ContainsTypeScript; + node.objectAssignmentInitializer = objectAssignmentInitializer; node.jsDoc = undefined; // initialized by parser (JsDocContainer) return node; diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 976c644e8509e..202ba415d0d8a 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -8268,26 +8268,13 @@ namespace Parser { const hasJSDoc = hasPrecedingJSDocComment(); const tokenIsIdentifier = isIdentifier(); const name = parsePropertyName(); - - const isShorthandPropertyAssignment = tokenIsIdentifier && (token() !== SyntaxKind.ColonToken); - let originalNode: ObjectLiteralElementLike; let initializer: Expression | undefined; - if (isShorthandPropertyAssignment) { - const equalsToken = parseOptionalToken(SyntaxKind.EqualsToken); - const objectAssignmentInitializer = equalsToken ? allowInAnd(() => parseAssignmentExpressionOrHigher(/*allowReturnTypeInArrowFunction*/ true)) : undefined; - const node: Mutable = originalNode = factory.createShorthandPropertyAssignment(name as Identifier, objectAssignmentInitializer); - // Save equals token for error reporting. - // TODO(rbuckton): Consider manufacturing this when we need to report an error as it is otherwise not useful. - node.equalsToken = equalsToken; - } - else { + + if (token() === SyntaxKind.ColonToken) { parseExpected(SyntaxKind.ColonToken); initializer = allowInAnd(() => parseAssignmentExpressionOrHigher(/*allowReturnTypeInArrowFunction*/ true)); - originalNode = factory.createPropertyAssignment(name, initializer); } - const node = withJSDoc(finishNode(factory.createEnumMember(name, initializer), pos), hasJSDoc); - setOriginalNode(node, originalNode); return node; } diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 506ba343ada7b..c34f3bfcf159e 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -1949,7 +1949,11 @@ export function transformTypeScript(context: TransformationContext): Transformer function visitEnumLiteralExpression(node: EnumLiteralExpression): ObjectLiteralExpression { const properties: readonly ObjectLiteralElementLike[] = node.members.map((member: EnumMember): ObjectLiteralElementLike => { - return member.original as ObjectLiteralElementLike; + if (member.initializer) { + return factory.createPropertyAssignment(member.name, member.initializer); + } else { + return factory.createShorthandPropertyAssignment(idText(member.name)); + } }); // return factory.updateEnumLiteralExpression( diff --git a/tests/baselines/reference/enumLiteralBasics.js b/tests/baselines/reference/enumLiteralBasics.js index a1671ef56873f..27ded39c3694a 100644 --- a/tests/baselines/reference/enumLiteralBasics.js +++ b/tests/baselines/reference/enumLiteralBasics.js @@ -46,20 +46,11 @@ const sval_bad2: E1 = functest("string"); // error // Enum literal syntax does not implement auto-incrementing behaviour. var ExistingShorthand = "exists"; var E1 = { - NonexistingShorthand: NonexistingShorthand// Enum without initializers have first member = 0 and successive members = N + 1 - // Enum literal syntax does not implement auto-incrementing behaviour. - , // Enum without initializers have first member = 0 and successive members = N + 1 - ExistingShorthand: ExistingShorthand// Enum without initializers have first member = 0 and successive members = N + 1 - // Enum literal syntax does not implement auto-incrementing behaviour. - , // Enum without initializers have first member = 0 and successive members = N + 1 - Int: 1// Enum without initializers have first member = 0 and successive members = N + 1 - // Enum literal syntax does not implement auto-incrementing behaviour. - , // Enum without initializers have first member = 0 and successive members = N + 1 - String: "string"// Enum without initializers have first member = 0 and successive members = N + 1 - // Enum literal syntax does not implement auto-incrementing behaviour. - , // Enum without initializers have first member = 0 and successive members = N + 1 - Flag: 8// Enum without initializers have first member = 0 and successive members = N + 1 - // Enum literal syntax does not implement auto-incrementing behaviour. + NonexistingShorthand: NonexistingShorthand, + ExistingShorthand: ExistingShorthand, + Int: 1, + String: "string", + Flag: 8 }; // Valid assignments var nonexist = E1.NonexistingShorthand; // ok From 85401e6b2ce027c2bfa693ed3419d8a37411ddc6 Mon Sep 17 00:00:00 2001 From: Caitlin Potter Date: Thu, 13 Mar 2025 12:57:03 -0400 Subject: [PATCH 08/14] Remove commented out lines, fix warnings and update typescript.d.ts to pass tests --- src/compiler/binder.ts | 9 - src/compiler/checker.ts | 4 +- src/compiler/emitter.ts | 14 - src/compiler/factory/nodeFactory.ts | 3 +- src/compiler/transformers/ts.ts | 2 +- tests/baselines/reference/api/typescript.d.ts | 280 +++++++++--------- 6 files changed, 152 insertions(+), 160 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 690ce2dd1f639..38f5d858044ce 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -3674,15 +3674,6 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void { varSymbol.flags &= ~(SymbolFlags.Assignment | SymbolFlags.Variable); varSymbol.exports ??= createSymbolTable(); appendIfUnique(varSymbol.declarations, node); - - // Temporarily treat `node` as the container - // const saveContainer = container; - // container = node; - // bindChildren(node); - // container = saveContainer; - // return isEnumConst(node) - // ? bindBlockScopedDeclaration(node, SymbolFlags.ConstEnum, SymbolFlags.ConstEnumExcludes) - // : bindBlockScopedDeclaration(node, SymbolFlags.RegularEnum, SymbolFlags.RegularEnumExcludes); } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4bb242c3d5e5b..9257b7e8a0340 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -41120,7 +41120,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { case SyntaxKind.ObjectLiteralExpression: return checkObjectLiteral(node as ObjectLiteralExpression, checkMode); case SyntaxKind.EnumLiteralExpression: - return checkEnumLiteralExpression(node as EnumLiteralExpression, checkMode); + return checkEnumLiteralExpression(node as EnumLiteralExpression); case SyntaxKind.PropertyAccessExpression: return checkPropertyAccessExpression(node as PropertyAccessExpression, checkMode); case SyntaxKind.QualifiedName: @@ -49515,7 +49515,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const symbol = getSymbolAtLocation(node); if (symbol) { if (symbol.valueDeclaration && isEnumLiteralDeclaration(symbol.valueDeclaration)) { - return getDeclaredTypeOfEnum(symbol.valueDeclaration.initializer!); + return getDeclaredTypeOfEnum(symbol); } return getTypeOfSymbol(symbol); } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index a331d538323ab..2bf986df72fbe 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2621,24 +2621,10 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri } function emitEnumLiteralExpression(node: EnumLiteralExpression) { - // pushNameGenerationScope(node); - // forEach(node.members, generateMemberNames); - - // const indentedFlag = getEmitFlags(node) & EmitFlags.Indented; - // if (indentedFlag) { - // increaseIndent(); - // } - writeSpace(); writePunctuation("{"); emitList(node, node.members, ListFormat.EnumMembers); writePunctuation("}"); - - // if (indentedFlag) { - // decreaseIndent(); - // } - - // popNameGenerationScope(node); } function emitPropertyAccessExpression(node: PropertyAccessExpression) { diff --git a/src/compiler/factory/nodeFactory.ts b/src/compiler/factory/nodeFactory.ts index 757706ac71938..61e4efb9f5034 100644 --- a/src/compiler/factory/nodeFactory.ts +++ b/src/compiler/factory/nodeFactory.ts @@ -6043,14 +6043,13 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode // // @api - function createEnumMember(name: string | PropertyName, initializer?: Expression, objectAssignmentInitializer?: Expression) { + function createEnumMember(name: string | PropertyName, initializer?: Expression) { const node = createBaseDeclaration(SyntaxKind.EnumMember); node.name = asName(name); node.initializer = initializer && parenthesizerRules().parenthesizeExpressionForDisallowedComma(initializer); node.transformFlags |= propagateChildFlags(node.name) | propagateChildFlags(node.initializer) | TransformFlags.ContainsTypeScript; - node.objectAssignmentInitializer = objectAssignmentInitializer; node.jsDoc = undefined; // initialized by parser (JsDocContainer) return node; diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index c34f3bfcf159e..587594691c9e0 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -1952,7 +1952,7 @@ export function transformTypeScript(context: TransformationContext): Transformer if (member.initializer) { return factory.createPropertyAssignment(member.name, member.initializer); } else { - return factory.createShorthandPropertyAssignment(idText(member.name)); + return factory.createShorthandPropertyAssignment(idText(member.name as Identifier)); } }); diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 2531c9ea0413d..8b58efb21cff1 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3903,130 +3903,131 @@ declare namespace ts { MetaProperty = 236, SyntheticExpression = 237, SatisfiesExpression = 238, - TemplateSpan = 239, - SemicolonClassElement = 240, - Block = 241, - EmptyStatement = 242, - VariableStatement = 243, - ExpressionStatement = 244, - IfStatement = 245, - DoStatement = 246, - WhileStatement = 247, - ForStatement = 248, - ForInStatement = 249, - ForOfStatement = 250, - ContinueStatement = 251, - BreakStatement = 252, - ReturnStatement = 253, - WithStatement = 254, - SwitchStatement = 255, - LabeledStatement = 256, - ThrowStatement = 257, - TryStatement = 258, - DebuggerStatement = 259, - VariableDeclaration = 260, - VariableDeclarationList = 261, - FunctionDeclaration = 262, - ClassDeclaration = 263, - InterfaceDeclaration = 264, - TypeAliasDeclaration = 265, - EnumDeclaration = 266, - ModuleDeclaration = 267, - ModuleBlock = 268, - CaseBlock = 269, - NamespaceExportDeclaration = 270, - ImportEqualsDeclaration = 271, - ImportDeclaration = 272, - ImportClause = 273, - NamespaceImport = 274, - NamedImports = 275, - ImportSpecifier = 276, - ExportAssignment = 277, - ExportDeclaration = 278, - NamedExports = 279, - NamespaceExport = 280, - ExportSpecifier = 281, - MissingDeclaration = 282, - ExternalModuleReference = 283, - JsxElement = 284, - JsxSelfClosingElement = 285, - JsxOpeningElement = 286, - JsxClosingElement = 287, - JsxFragment = 288, - JsxOpeningFragment = 289, - JsxClosingFragment = 290, - JsxAttribute = 291, - JsxAttributes = 292, - JsxSpreadAttribute = 293, - JsxExpression = 294, - JsxNamespacedName = 295, - CaseClause = 296, - DefaultClause = 297, - HeritageClause = 298, - CatchClause = 299, - ImportAttributes = 300, - ImportAttribute = 301, - /** @deprecated */ AssertClause = 300, - /** @deprecated */ AssertEntry = 301, - /** @deprecated */ ImportTypeAssertionContainer = 302, - PropertyAssignment = 303, - ShorthandPropertyAssignment = 304, - SpreadAssignment = 305, - EnumMember = 306, - SourceFile = 307, - Bundle = 308, - JSDocTypeExpression = 309, - JSDocNameReference = 310, - JSDocMemberName = 311, - JSDocAllType = 312, - JSDocUnknownType = 313, - JSDocNullableType = 314, - JSDocNonNullableType = 315, - JSDocOptionalType = 316, - JSDocFunctionType = 317, - JSDocVariadicType = 318, - JSDocNamepathType = 319, - JSDoc = 320, + EnumLiteralExpression = 239, + TemplateSpan = 240, + SemicolonClassElement = 241, + Block = 242, + EmptyStatement = 243, + VariableStatement = 244, + ExpressionStatement = 245, + IfStatement = 246, + DoStatement = 247, + WhileStatement = 248, + ForStatement = 249, + ForInStatement = 250, + ForOfStatement = 251, + ContinueStatement = 252, + BreakStatement = 253, + ReturnStatement = 254, + WithStatement = 255, + SwitchStatement = 256, + LabeledStatement = 257, + ThrowStatement = 258, + TryStatement = 259, + DebuggerStatement = 260, + VariableDeclaration = 261, + VariableDeclarationList = 262, + FunctionDeclaration = 263, + ClassDeclaration = 264, + InterfaceDeclaration = 265, + TypeAliasDeclaration = 266, + EnumDeclaration = 267, + ModuleDeclaration = 268, + ModuleBlock = 269, + CaseBlock = 270, + NamespaceExportDeclaration = 271, + ImportEqualsDeclaration = 272, + ImportDeclaration = 273, + ImportClause = 274, + NamespaceImport = 275, + NamedImports = 276, + ImportSpecifier = 277, + ExportAssignment = 278, + ExportDeclaration = 279, + NamedExports = 280, + NamespaceExport = 281, + ExportSpecifier = 282, + MissingDeclaration = 283, + ExternalModuleReference = 284, + JsxElement = 285, + JsxSelfClosingElement = 286, + JsxOpeningElement = 287, + JsxClosingElement = 288, + JsxFragment = 289, + JsxOpeningFragment = 290, + JsxClosingFragment = 291, + JsxAttribute = 292, + JsxAttributes = 293, + JsxSpreadAttribute = 294, + JsxExpression = 295, + JsxNamespacedName = 296, + CaseClause = 297, + DefaultClause = 298, + HeritageClause = 299, + CatchClause = 300, + ImportAttributes = 301, + ImportAttribute = 302, + /** @deprecated */ AssertClause = 301, + /** @deprecated */ AssertEntry = 302, + /** @deprecated */ ImportTypeAssertionContainer = 303, + PropertyAssignment = 304, + ShorthandPropertyAssignment = 305, + SpreadAssignment = 306, + EnumMember = 307, + SourceFile = 308, + Bundle = 309, + JSDocTypeExpression = 310, + JSDocNameReference = 311, + JSDocMemberName = 312, + JSDocAllType = 313, + JSDocUnknownType = 314, + JSDocNullableType = 315, + JSDocNonNullableType = 316, + JSDocOptionalType = 317, + JSDocFunctionType = 318, + JSDocVariadicType = 319, + JSDocNamepathType = 320, + JSDoc = 321, /** @deprecated Use SyntaxKind.JSDoc */ - JSDocComment = 320, - JSDocText = 321, - JSDocTypeLiteral = 322, - JSDocSignature = 323, - JSDocLink = 324, - JSDocLinkCode = 325, - JSDocLinkPlain = 326, - JSDocTag = 327, - JSDocAugmentsTag = 328, - JSDocImplementsTag = 329, - JSDocAuthorTag = 330, - JSDocDeprecatedTag = 331, - JSDocClassTag = 332, - JSDocPublicTag = 333, - JSDocPrivateTag = 334, - JSDocProtectedTag = 335, - JSDocReadonlyTag = 336, - JSDocOverrideTag = 337, - JSDocCallbackTag = 338, - JSDocOverloadTag = 339, - JSDocEnumTag = 340, - JSDocParameterTag = 341, - JSDocReturnTag = 342, - JSDocThisTag = 343, - JSDocTypeTag = 344, - JSDocTemplateTag = 345, - JSDocTypedefTag = 346, - JSDocSeeTag = 347, - JSDocPropertyTag = 348, - JSDocThrowsTag = 349, - JSDocSatisfiesTag = 350, - JSDocImportTag = 351, - SyntaxList = 352, - NotEmittedStatement = 353, - NotEmittedTypeElement = 354, - PartiallyEmittedExpression = 355, - CommaListExpression = 356, - SyntheticReferenceExpression = 357, - Count = 358, + JSDocComment = 321, + JSDocText = 322, + JSDocTypeLiteral = 323, + JSDocSignature = 324, + JSDocLink = 325, + JSDocLinkCode = 326, + JSDocLinkPlain = 327, + JSDocTag = 328, + JSDocAugmentsTag = 329, + JSDocImplementsTag = 330, + JSDocAuthorTag = 331, + JSDocDeprecatedTag = 332, + JSDocClassTag = 333, + JSDocPublicTag = 334, + JSDocPrivateTag = 335, + JSDocProtectedTag = 336, + JSDocReadonlyTag = 337, + JSDocOverrideTag = 338, + JSDocCallbackTag = 339, + JSDocOverloadTag = 340, + JSDocEnumTag = 341, + JSDocParameterTag = 342, + JSDocReturnTag = 343, + JSDocThisTag = 344, + JSDocTypeTag = 345, + JSDocTemplateTag = 346, + JSDocTypedefTag = 347, + JSDocSeeTag = 348, + JSDocPropertyTag = 349, + JSDocThrowsTag = 350, + JSDocSatisfiesTag = 351, + JSDocImportTag = 352, + SyntaxList = 353, + NotEmittedStatement = 354, + NotEmittedTypeElement = 355, + PartiallyEmittedExpression = 356, + CommaListExpression = 357, + SyntheticReferenceExpression = 358, + Count = 359, FirstAssignment = 64, LastAssignment = 79, FirstCompoundAssignment = 65, @@ -4051,13 +4052,13 @@ declare namespace ts { LastTemplateToken = 18, FirstBinaryOperator = 30, LastBinaryOperator = 79, - FirstStatement = 243, - LastStatement = 259, + FirstStatement = 244, + LastStatement = 260, FirstNode = 166, - FirstJSDocNode = 309, - LastJSDocNode = 351, - FirstJSDocTagNode = 327, - LastJSDocTagNode = 351, + FirstJSDocNode = 310, + LastJSDocNode = 352, + FirstJSDocTagNode = 328, + LastJSDocTagNode = 352, } type TriviaSyntaxKind = SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia | SyntaxKind.NewLineTrivia | SyntaxKind.WhitespaceTrivia | SyntaxKind.ShebangTrivia | SyntaxKind.ConflictMarkerTrivia; type LiteralSyntaxKind = SyntaxKind.NumericLiteral | SyntaxKind.BigIntLiteral | SyntaxKind.StringLiteral | SyntaxKind.JsxText | SyntaxKind.JsxTextAllWhiteSpaces | SyntaxKind.RegularExpressionLiteral | SyntaxKind.NoSubstitutionTemplateLiteral; @@ -4361,6 +4362,7 @@ declare namespace ts { | NamedTupleMember | NamespaceExportDeclaration | ObjectLiteralExpression + | EnumLiteralExpression | ParameterDeclaration | ParenthesizedExpression | PropertyAccessExpression @@ -4385,7 +4387,7 @@ declare namespace ts { type HasInitializer = HasExpressionInitializer | ForStatement | ForInStatement | ForOfStatement | JsxAttribute; type HasExpressionInitializer = VariableDeclaration | ParameterDeclaration | BindingElement | PropertyDeclaration | PropertyAssignment | EnumMember; type HasDecorators = ParameterDeclaration | PropertyDeclaration | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration | ClassExpression | ClassDeclaration; - type HasModifiers = TypeParameterDeclaration | ParameterDeclaration | ConstructorTypeNode | PropertySignature | PropertyDeclaration | MethodSignature | MethodDeclaration | ConstructorDeclaration | GetAccessorDeclaration | SetAccessorDeclaration | IndexSignatureDeclaration | FunctionExpression | ArrowFunction | ClassExpression | VariableStatement | FunctionDeclaration | ClassDeclaration | InterfaceDeclaration | TypeAliasDeclaration | EnumDeclaration | ModuleDeclaration | ImportEqualsDeclaration | ImportDeclaration | ExportAssignment | ExportDeclaration; + type HasModifiers = TypeParameterDeclaration | ParameterDeclaration | ConstructorTypeNode | PropertySignature | PropertyDeclaration | MethodSignature | MethodDeclaration | ConstructorDeclaration | GetAccessorDeclaration | SetAccessorDeclaration | IndexSignatureDeclaration | FunctionExpression | ArrowFunction | ClassExpression | VariableStatement | FunctionDeclaration | ClassDeclaration | InterfaceDeclaration | TypeAliasDeclaration | EnumDeclaration | EnumLiteralExpression | ModuleDeclaration | ImportEqualsDeclaration | ImportDeclaration | ExportAssignment | ExportDeclaration; interface NodeArray extends ReadonlyArray, ReadonlyTextRange { readonly hasTrailingComma: boolean; } @@ -5438,7 +5440,7 @@ declare namespace ts { } interface EnumMember extends NamedDeclaration, JSDocContainer { readonly kind: SyntaxKind.EnumMember; - readonly parent: EnumDeclaration; + readonly parent: EnumDeclarationType; readonly name: PropertyName; readonly initializer?: Expression; } @@ -5448,6 +5450,14 @@ declare namespace ts { readonly name: Identifier; readonly members: NodeArray; } + interface EnumLiteralExpression extends PrimaryExpression, Declaration, JSDocContainer { + readonly kind: SyntaxKind.EnumLiteralExpression; + readonly parent: VariableDeclaration; + readonly modifiers?: NodeArray; + readonly name: Identifier; + readonly members: NodeArray; + } + type EnumDeclarationType = EnumDeclaration | EnumLiteralExpression; type ModuleName = Identifier | StringLiteral; type ModuleBody = NamespaceBody | JSDocNamespaceBody; interface ModuleDeclaration extends DeclarationStatement, JSDocContainer, LocalsContainer { @@ -5953,7 +5963,7 @@ declare namespace ts { readonly operator: SyntaxKind.MinusToken; readonly operand: NumericLiteral; } - type JsonObjectExpression = ObjectLiteralExpression | ArrayLiteralExpression | JsonMinusNumericLiteral | NumericLiteral | StringLiteral | BooleanLiteral | NullLiteral; + type JsonObjectExpression = ObjectLiteralExpression | EnumLiteralExpression | ArrayLiteralExpression | JsonMinusNumericLiteral | NumericLiteral | StringLiteral | BooleanLiteral | NullLiteral; interface JsonObjectExpressionStatement extends ExpressionStatement { readonly expression: JsonObjectExpression; } @@ -7701,6 +7711,8 @@ declare namespace ts { updateTypeAliasDeclaration(node: TypeAliasDeclaration, modifiers: readonly ModifierLike[] | undefined, name: Identifier, typeParameters: readonly TypeParameterDeclaration[] | undefined, type: TypeNode): TypeAliasDeclaration; createEnumDeclaration(modifiers: readonly ModifierLike[] | undefined, name: string | Identifier, members: readonly EnumMember[]): EnumDeclaration; updateEnumDeclaration(node: EnumDeclaration, modifiers: readonly ModifierLike[] | undefined, name: Identifier, members: readonly EnumMember[]): EnumDeclaration; + createEnumLiteralExpression(modifiers: readonly ModifierLike[] | undefined, name: string | Identifier, members: readonly EnumMember[]): EnumLiteralExpression; + updateEnumLiteralExpression(node: EnumLiteralExpression, modifiers: readonly ModifierLike[] | undefined, name: Identifier, members: readonly EnumMember[]): EnumLiteralExpression; createModuleDeclaration(modifiers: readonly ModifierLike[] | undefined, name: ModuleName, body: ModuleBody | undefined, flags?: NodeFlags): ModuleDeclaration; updateModuleDeclaration(node: ModuleDeclaration, modifiers: readonly ModifierLike[] | undefined, name: ModuleName, body: ModuleBody | undefined): ModuleDeclaration; createModuleBlock(statements: readonly Statement[]): ModuleBlock; @@ -8727,6 +8739,9 @@ declare namespace ts { function isOptionalChain(node: Node): node is PropertyAccessChain | ElementAccessChain | CallChain | NonNullChain; function isNullishCoalesce(node: Node): boolean; function isConstTypeReference(node: Node): boolean; + function isEnumTypeReference(node: Node): boolean; + function isEnumLiteralDeclaration(node: Node): boolean; + function isEnumTypeAnnotation(node: Node): boolean; function skipPartiallyEmittedExpressions(node: Expression): Expression; function skipPartiallyEmittedExpressions(node: Node): Node; function isNonNullChain(node: Node): node is NonNullChain; @@ -9040,6 +9055,7 @@ declare namespace ts { function isInterfaceDeclaration(node: Node): node is InterfaceDeclaration; function isTypeAliasDeclaration(node: Node): node is TypeAliasDeclaration; function isEnumDeclaration(node: Node): node is EnumDeclaration; + function isEnumLiteralExpression(node: Node): node is EnumLiteralExpression; function isModuleDeclaration(node: Node): node is ModuleDeclaration; function isModuleBlock(node: Node): node is ModuleBlock; function isCaseBlock(node: Node): node is CaseBlock; From d002e9e43daa02006c8f4cf153ba83c9b66dbca7 Mon Sep 17 00:00:00 2001 From: Caitlin Potter Date: Thu, 13 Mar 2025 13:38:52 -0400 Subject: [PATCH 09/14] run hereby format --- src/compiler/binder.ts | 1 - src/compiler/checker.ts | 12 ++++++------ src/compiler/transformers/ts.ts | 3 ++- src/compiler/utilities.ts | 2 +- src/compiler/visitorPublic.ts | 1 - 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 38f5d858044ce..d3dadfd5a24d9 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -3676,7 +3676,6 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void { appendIfUnique(varSymbol.declarations, node); } - function bindVariableDeclarationOrBindingElement(node: VariableDeclaration | BindingElement) { if (inStrictMode) { checkStrictModeEvalOrArguments(node, node.name); diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9257b7e8a0340..89639e504bc4c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -169,6 +169,7 @@ import { EntityNameOrEntityNameExpression, entityNameToString, EnumDeclaration, + EnumLiteralExpression, EnumMember, EnumType, equateValues, @@ -536,7 +537,11 @@ import { isEntityNameExpression, isEnumConst, isEnumDeclaration, + isEnumLiteralDeclaration, + isEnumLiteralExpression, isEnumMember, + isEnumTypeAnnotation, + isEnumTypeReference, isExclusivelyTypeOnlyImportOrExport, isExpandoPropertyDeclaration, isExportAssignment, @@ -1133,11 +1138,6 @@ import { WideningContext, WithStatement, YieldExpression, - EnumLiteralExpression, - isEnumLiteralExpression, - isEnumTypeReference, - isEnumTypeAnnotation, - isEnumLiteralDeclaration, } from "./_namespaces/ts.js"; import * as moduleSpecifiers from "./_namespaces/ts.moduleSpecifiers.js"; import * as performance from "./_namespaces/ts.performance.js"; @@ -47045,7 +47045,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } - function computeEnumMemberValues(node: EnumDeclaration|EnumLiteralExpression) { + function computeEnumMemberValues(node: EnumDeclaration | EnumLiteralExpression) { const nodeLinks = getNodeLinks(node); if (!(nodeLinks.flags & NodeCheckFlags.EnumValuesComputed)) { nodeLinks.flags |= NodeCheckFlags.EnumValuesComputed; diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 587594691c9e0..12a06b8cb9284 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -1951,7 +1951,8 @@ export function transformTypeScript(context: TransformationContext): Transformer const properties: readonly ObjectLiteralElementLike[] = node.members.map((member: EnumMember): ObjectLiteralElementLike => { if (member.initializer) { return factory.createPropertyAssignment(member.name, member.initializer); - } else { + } + else { return factory.createShorthandPropertyAssignment(idText(member.name as Identifier)); } }); diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 3877687ff545d..7f5ddd6b2f951 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -6809,7 +6809,7 @@ export function getAllAccessorDeclarations(declarations: readonly Declaration[] export function getEffectiveTypeAnnotationNode(node: Node): TypeNode | undefined { if (!isInJSFile(node) && isFunctionDeclaration(node)) return undefined; if (isTypeAliasDeclaration(node)) return undefined; // has a .type, is not a type annotation - if (isEnumLiteralDeclaration(node)) return undefined;// has a type reference, but is not used as a hint about the meaning of the initializer. + if (isEnumLiteralDeclaration(node)) return undefined; // has a type reference, but is not used as a hint about the meaning of the initializer. const type = (node as HasType).type; if (type || !isInJSFile(node)) return type; return isJSDocPropertyLikeTag(node) ? node.typeExpression && node.typeExpression.type : getJSDocType(node); diff --git a/src/compiler/visitorPublic.ts b/src/compiler/visitorPublic.ts index 7dc82c1518b02..4f15d1452899e 100644 --- a/src/compiler/visitorPublic.ts +++ b/src/compiler/visitorPublic.ts @@ -1489,7 +1489,6 @@ const visitEachChildTable: VisitEachChildTable = { ); }, - [SyntaxKind.ModuleDeclaration]: function visitEachChildOfModuleDeclaration(node, visitor, context, nodesVisitor, nodeVisitor, _tokenVisitor) { return context.factory.updateModuleDeclaration( node, From 303bf2547cbf5d379470a46c8250e28bcedbe6a9 Mon Sep 17 00:00:00 2001 From: Caitlin Potter Date: Fri, 14 Mar 2025 10:57:21 -0400 Subject: [PATCH 10/14] Fix conformance tests, add additional test cases to enumLiteralBaiscs.ts for individual members as types --- src/compiler/factory/nodeFactory.ts | 7 +- src/compiler/parser.ts | 11 +- src/compiler/types.ts | 6 +- .../reference/enumLiteralBasics.errors.txt | 48 +++- .../baselines/reference/enumLiteralBasics.js | 37 ++- .../reference/enumLiteralBasics.symbols | 212 ++++++++++++++---- .../reference/enumLiteralBasics.types | 207 ++++++++++++++++- .../reference/enumLiteralSimple.symbols | 5 +- .../reference/enumLiteralSimple.types | 2 - .../conformance/enums/enumLiteralBasics.ts | 19 ++ 10 files changed, 480 insertions(+), 74 deletions(-) diff --git a/src/compiler/factory/nodeFactory.ts b/src/compiler/factory/nodeFactory.ts index 61e4efb9f5034..26eec22ab331e 100644 --- a/src/compiler/factory/nodeFactory.ts +++ b/src/compiler/factory/nodeFactory.ts @@ -4546,15 +4546,14 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode // @api function createEnumLiteralExpression( modifiers: readonly ModifierLike[] | undefined, - name: string | Identifier, + name: __String, members: readonly EnumMember[], ) { const node = createBaseDeclaration(SyntaxKind.EnumLiteralExpression); node.modifiers = asNodeArray(modifiers); - node.name = asName(name); + node.name = name; node.members = createNodeArray(members); node.transformFlags |= propagateChildrenFlags(node.modifiers) | - propagateChildFlags(node.name) | propagateChildrenFlags(node.members) | TransformFlags.ContainsTypeScript; node.transformFlags &= ~TransformFlags.ContainsPossibleTopLevelAwait; // Enum declarations cannot contain `await` @@ -4567,7 +4566,7 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode function updateEnumLiteralExpression( node: EnumLiteralExpression, modifiers: readonly ModifierLike[] | undefined, - name: Identifier, + name: __String, members: readonly EnumMember[], ) { return node.modifiers !== modifiers diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 202ba415d0d8a..dd476584436c8 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -110,6 +110,7 @@ import { getLeadingCommentRanges, getSpellingSuggestion, getTextOfNodeFromSourceText, + getTokenPosOfNode, HasJSDoc, hasJSDocNodes, HasModifiers, @@ -923,8 +924,9 @@ const forEachChildTable: ForEachChildTable = { visitNode(cbNode, node.initializer); }, [SyntaxKind.EnumLiteralExpression]: function forEachChildInEnumLiteralExpression(node: EnumLiteralExpression, cbNode: (node: Node) => T | undefined, cbNodes?: (nodes: NodeArray) => T | undefined): T | undefined { + // For EnumLiteralExpression, the name is not visited or emitted. It is a reference to the binding name of a Declaration for which + // the EnumLiteralExpression is an initializer, and is used to permit diagnostic code for EnumDeclarations to be used. return visitNodes(cbNode, cbNodes, node.modifiers) || - visitNode(cbNode, node.name) || visitNodes(cbNode, cbNodes, node.members); }, [SyntaxKind.ModuleDeclaration]: function forEachChildInModuleDeclaration(node: ModuleDeclaration, cbNode: (node: Node) => T | undefined, cbNodes?: (nodes: NodeArray) => T | undefined): T | undefined { @@ -7645,7 +7647,7 @@ namespace Parser { } const type = parseTypeAnnotation(); if (allowEnumType && type && isEnumTypeReference(type) && name.kind === SyntaxKind.Identifier) { - const initializer = parseInitializerAsEnumLiteralExpression(name, pos); + const initializer = parseInitializerAsEnumLiteralExpression(name); const node = factoryCreateVariableDeclaration(name, exclamationToken, type, initializer); return withJSDoc(finishNode(node, pos), hasJSDoc); } @@ -8293,16 +8295,17 @@ namespace Parser { return withJSDoc(finishNode(node, pos), hasJSDoc); } - function parseInitializerAsEnumLiteralExpression(name: Identifier, pos: number) { + function parseInitializerAsEnumLiteralExpression(name: Identifier) { // A VariableDeclaration with a declared type `enum` expects the initializer to be in a format like an ObjectLiteralExpression, // with only constant keys and property assignments, which are treated as EnumMembers. + const pos = getNodePos(); parseExpected(SyntaxKind.EqualsToken); parseExpected(SyntaxKind.OpenBraceToken); const members = parseDelimitedList(ParsingContext.ObjectLiteralMembers, parseObjectLiteralEnumMember); parseExpected(SyntaxKind.CloseBraceToken); // Should the variable have the jsdoc, or the enum literal, or both? - return finishNode(factory.createEnumLiteralExpression(/*modifiers*/ undefined, name, members), pos); + return finishNode(factory.createEnumLiteralExpression(/*modifiers*/ undefined, name.escapedText, members), pos); } function parseModuleBlock(): ModuleBlock { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 379d95d096eea..a6415069d8e2a 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3616,7 +3616,7 @@ export interface EnumLiteralExpression extends PrimaryExpression, Declaration, J readonly kind: SyntaxKind.EnumLiteralExpression; readonly parent: VariableDeclaration; readonly modifiers?: NodeArray; - readonly name: Identifier; // For compatibility with EnumDeclaration -- however this must be equal to the BindingName in the VariableDeclaration. + readonly name: __String; // For compatibility with EnumDeclaration -- however this must be equal to the BindingName in the VariableDeclaration. readonly members: NodeArray; } @@ -9025,8 +9025,8 @@ export interface NodeFactory { updateTypeAliasDeclaration(node: TypeAliasDeclaration, modifiers: readonly ModifierLike[] | undefined, name: Identifier, typeParameters: readonly TypeParameterDeclaration[] | undefined, type: TypeNode): TypeAliasDeclaration; createEnumDeclaration(modifiers: readonly ModifierLike[] | undefined, name: string | Identifier, members: readonly EnumMember[]): EnumDeclaration; updateEnumDeclaration(node: EnumDeclaration, modifiers: readonly ModifierLike[] | undefined, name: Identifier, members: readonly EnumMember[]): EnumDeclaration; - createEnumLiteralExpression(modifiers: readonly ModifierLike[] | undefined, name: string | Identifier, members: readonly EnumMember[]): EnumLiteralExpression; - updateEnumLiteralExpression(node: EnumLiteralExpression, modifiers: readonly ModifierLike[] | undefined, name: Identifier, members: readonly EnumMember[]): EnumLiteralExpression; + createEnumLiteralExpression(modifiers: readonly ModifierLike[] | undefined, name: __String, members: readonly EnumMember[]): EnumLiteralExpression; + updateEnumLiteralExpression(node: EnumLiteralExpression, modifiers: readonly ModifierLike[] | undefined, name: __String, members: readonly EnumMember[]): EnumLiteralExpression; createModuleDeclaration(modifiers: readonly ModifierLike[] | undefined, name: ModuleName, body: ModuleBody | undefined, flags?: NodeFlags): ModuleDeclaration; updateModuleDeclaration(node: ModuleDeclaration, modifiers: readonly ModifierLike[] | undefined, name: ModuleName, body: ModuleBody | undefined): ModuleDeclaration; createModuleBlock(statements: readonly Statement[]): ModuleBlock; diff --git a/tests/baselines/reference/enumLiteralBasics.errors.txt b/tests/baselines/reference/enumLiteralBasics.errors.txt index fed2f2741a8b1..d8c9fbfe01197 100644 --- a/tests/baselines/reference/enumLiteralBasics.errors.txt +++ b/tests/baselines/reference/enumLiteralBasics.errors.txt @@ -1,12 +1,18 @@ enumLiteralBasics.ts(6,5): error TS1061: Enum member must have initializer. enumLiteralBasics.ts(7,5): error TS1061: Enum member must have initializer. -enumLiteralBasics.ts(21,7): error TS2322: Type '"exists"' is not assignable to type 'E1'. -enumLiteralBasics.ts(23,7): error TS2322: Type '"string"' is not assignable to type 'E1'. -enumLiteralBasics.ts(33,33): error TS2345: Argument of type '"exists"' is not assignable to parameter of type 'E1'. -enumLiteralBasics.ts(38,32): error TS2345: Argument of type '"string"' is not assignable to parameter of type 'E1'. +enumLiteralBasics.ts(22,1): error TS2322: Type 'E1.Flag' is not assignable to type 'E1.Int'. +enumLiteralBasics.ts(27,7): error TS2322: Type '"exists"' is not assignable to type 'E1'. +enumLiteralBasics.ts(29,7): error TS2322: Type '"string"' is not assignable to type 'E1'. +enumLiteralBasics.ts(39,33): error TS2345: Argument of type '"exists"' is not assignable to parameter of type 'E1'. +enumLiteralBasics.ts(44,32): error TS2345: Argument of type '"string"' is not assignable to parameter of type 'E1'. +enumLiteralBasics.ts(52,38): error TS2345: Argument of type '"exists"' is not assignable to parameter of type 'E1.Int'. +enumLiteralBasics.ts(54,38): error TS2345: Argument of type '4' is not assignable to parameter of type 'E1.Int'. +enumLiteralBasics.ts(56,38): error TS2345: Argument of type 'E1.String' is not assignable to parameter of type 'E1.Int'. +enumLiteralBasics.ts(57,7): error TS2322: Type 'E1.Int' is not assignable to type 'E1.Flag'. +enumLiteralBasics.ts(57,38): error TS2345: Argument of type '"string"' is not assignable to parameter of type 'E1.Int'. -==== enumLiteralBasics.ts (6 errors) ==== +==== enumLiteralBasics.ts (12 errors) ==== // Enum without initializers have first member = 0 and successive members = N + 1 // Enum literal syntax does not implement auto-incrementing behaviour. @@ -28,6 +34,14 @@ enumLiteralBasics.ts(38,32): error TS2345: Argument of type '"string"' is not as const exist: E1 = E1.ExistingShorthand; // ok const ival: E1 = E1.Int; // ok const sval: E1 = E1.String; // ok + let p_int: E1.Int = E1.Int; // ok + const p_nonexist: E1.NonexistingShorthand = E1.NonexistingShorthand; // ok + const p_exist: E1.ExistingShorthand = E1.ExistingShorthand; // ok + const p_string: E1.String = E1.String; // ok + p_int = E1.Flag; // Type 'E1.Flag' is not assignable to type 'E1.Int'. + ~~~~~ +!!! error TS2322: Type 'E1.Flag' is not assignable to type 'E1.Int'. + p_int = E1.Int | E1.Flag; // Assigning values which are not part of the enum despite being present in the enum const nonexist_bad: E1 = undefined; // error @@ -57,4 +71,26 @@ enumLiteralBasics.ts(38,32): error TS2345: Argument of type '"string"' is not as const sval_bad2: E1 = functest("string"); // error ~~~~~~~~ !!! error TS2345: Argument of type '"string"' is not assignable to parameter of type 'E1'. - \ No newline at end of file + + function functest2(value: E1.Int) { + console.log(value); + return value; + } + + const nonexist_bad3: E1.Int = functest2(undefined); + const exist_bad3: E1.Int = functest2("exists"); // error + ~~~~~~~~ +!!! error TS2345: Argument of type '"exists"' is not assignable to parameter of type 'E1.Int'. + const ival_good5: E1.Int = functest2(1); // ok + const ival_good6: E1.Int = functest2(4); // ok + ~ +!!! error TS2345: Argument of type '4' is not assignable to parameter of type 'E1.Int'. + const ival_good7: E1.Int = functest2(E1.Int | E1.Flag); // ok + const sval_good3: E1.Int = functest2(E1.String); + ~~~~~~~~~ +!!! error TS2345: Argument of type 'E1.String' is not assignable to parameter of type 'E1.Int'. + const sval_bad3: E1.Flag = functest2("string"); // error + ~~~~~~~~~ +!!! error TS2322: Type 'E1.Int' is not assignable to type 'E1.Flag'. + ~~~~~~~~ +!!! error TS2345: Argument of type '"string"' is not assignable to parameter of type 'E1.Int'. \ No newline at end of file diff --git a/tests/baselines/reference/enumLiteralBasics.js b/tests/baselines/reference/enumLiteralBasics.js index 27ded39c3694a..746a45d16e91a 100644 --- a/tests/baselines/reference/enumLiteralBasics.js +++ b/tests/baselines/reference/enumLiteralBasics.js @@ -18,6 +18,12 @@ const nonexist: E1 = E1.NonexistingShorthand; // ok const exist: E1 = E1.ExistingShorthand; // ok const ival: E1 = E1.Int; // ok const sval: E1 = E1.String; // ok +let p_int: E1.Int = E1.Int; // ok +const p_nonexist: E1.NonexistingShorthand = E1.NonexistingShorthand; // ok +const p_exist: E1.ExistingShorthand = E1.ExistingShorthand; // ok +const p_string: E1.String = E1.String; // ok +p_int = E1.Flag; // Type 'E1.Flag' is not assignable to type 'E1.Int'. +p_int = E1.Int | E1.Flag; // Assigning values which are not part of the enum despite being present in the enum const nonexist_bad: E1 = undefined; // error @@ -39,7 +45,19 @@ const ival_good3: E1 = functest(4); // ok const ival_good4: E1 = functest(E1.Int | E1.Flag); // ok const sval_good2: E1 = functest(E1.String); const sval_bad2: E1 = functest("string"); // error - + +function functest2(value: E1.Int) { + console.log(value); + return value; +} + +const nonexist_bad3: E1.Int = functest2(undefined); +const exist_bad3: E1.Int = functest2("exists"); // error +const ival_good5: E1.Int = functest2(1); // ok +const ival_good6: E1.Int = functest2(4); // ok +const ival_good7: E1.Int = functest2(E1.Int | E1.Flag); // ok +const sval_good3: E1.Int = functest2(E1.String); +const sval_bad3: E1.Flag = functest2("string"); // error //// [enumLiteralBasics.js] // Enum without initializers have first member = 0 and successive members = N + 1 @@ -57,6 +75,12 @@ var nonexist = E1.NonexistingShorthand; // ok var exist = E1.ExistingShorthand; // ok var ival = E1.Int; // ok var sval = E1.String; // ok +var p_int = E1.Int; // ok +var p_nonexist = E1.NonexistingShorthand; // ok +var p_exist = E1.ExistingShorthand; // ok +var p_string = E1.String; // ok +p_int = E1.Flag; // Type 'E1.Flag' is not assignable to type 'E1.Int'. +p_int = E1.Int | E1.Flag; // Assigning values which are not part of the enum despite being present in the enum var nonexist_bad = undefined; // error var exist_bad = "exists"; // error @@ -74,3 +98,14 @@ var ival_good3 = functest(4); // ok var ival_good4 = functest(E1.Int | E1.Flag); // ok var sval_good2 = functest(E1.String); var sval_bad2 = functest("string"); // error +function functest2(value) { + console.log(value); + return value; +} +var nonexist_bad3 = functest2(undefined); +var exist_bad3 = functest2("exists"); // error +var ival_good5 = functest2(1); // ok +var ival_good6 = functest2(4); // ok +var ival_good7 = functest2(E1.Int | E1.Flag); // ok +var sval_good3 = functest2(E1.String); +var sval_bad3 = functest2("string"); // error diff --git a/tests/baselines/reference/enumLiteralBasics.symbols b/tests/baselines/reference/enumLiteralBasics.symbols index 966e00ece36f0..55325c930e8b0 100644 --- a/tests/baselines/reference/enumLiteralBasics.symbols +++ b/tests/baselines/reference/enumLiteralBasics.symbols @@ -8,8 +8,7 @@ let ExistingShorthand = "exists"; >ExistingShorthand : Symbol(ExistingShorthand, Decl(enumLiteralBasics.ts, 3, 3)) const E1: enum = { ->E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) ->E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) NonexistingShorthand, // error -- EnumLiteralExpressions require explicit property definitions. >NonexistingShorthand : Symbol(E1.NonexistingShorthand, Decl(enumLiteralBasics.ts, 4, 18)) @@ -31,111 +30,226 @@ const E1: enum = { // Valid assignments const nonexist: E1 = E1.NonexistingShorthand; // ok >nonexist : Symbol(nonexist, Decl(enumLiteralBasics.ts, 13, 5)) ->E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) >E1.NonexistingShorthand : Symbol(E1.NonexistingShorthand, Decl(enumLiteralBasics.ts, 4, 18)) ->E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) >NonexistingShorthand : Symbol(E1.NonexistingShorthand, Decl(enumLiteralBasics.ts, 4, 18)) const exist: E1 = E1.ExistingShorthand; // ok >exist : Symbol(exist, Decl(enumLiteralBasics.ts, 14, 5)) ->E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) >E1.ExistingShorthand : Symbol(E1.ExistingShorthand, Decl(enumLiteralBasics.ts, 5, 25)) ->E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) >ExistingShorthand : Symbol(E1.ExistingShorthand, Decl(enumLiteralBasics.ts, 5, 25)) const ival: E1 = E1.Int; // ok >ival : Symbol(ival, Decl(enumLiteralBasics.ts, 15, 5)) ->E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) >E1.Int : Symbol(E1.Int, Decl(enumLiteralBasics.ts, 6, 22)) ->E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) >Int : Symbol(E1.Int, Decl(enumLiteralBasics.ts, 6, 22)) const sval: E1 = E1.String; // ok >sval : Symbol(sval, Decl(enumLiteralBasics.ts, 16, 5)) ->E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) >E1.String : Symbol(E1.String, Decl(enumLiteralBasics.ts, 7, 11)) ->E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) >String : Symbol(E1.String, Decl(enumLiteralBasics.ts, 7, 11)) +let p_int: E1.Int = E1.Int; // ok +>p_int : Symbol(p_int, Decl(enumLiteralBasics.ts, 17, 3)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) +>Int : Symbol(E1.Int, Decl(enumLiteralBasics.ts, 6, 22)) +>E1.Int : Symbol(E1.Int, Decl(enumLiteralBasics.ts, 6, 22)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) +>Int : Symbol(E1.Int, Decl(enumLiteralBasics.ts, 6, 22)) + +const p_nonexist: E1.NonexistingShorthand = E1.NonexistingShorthand; // ok +>p_nonexist : Symbol(p_nonexist, Decl(enumLiteralBasics.ts, 18, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) +>NonexistingShorthand : Symbol(E1.NonexistingShorthand, Decl(enumLiteralBasics.ts, 4, 18)) +>E1.NonexistingShorthand : Symbol(E1.NonexistingShorthand, Decl(enumLiteralBasics.ts, 4, 18)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) +>NonexistingShorthand : Symbol(E1.NonexistingShorthand, Decl(enumLiteralBasics.ts, 4, 18)) + +const p_exist: E1.ExistingShorthand = E1.ExistingShorthand; // ok +>p_exist : Symbol(p_exist, Decl(enumLiteralBasics.ts, 19, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) +>ExistingShorthand : Symbol(E1.ExistingShorthand, Decl(enumLiteralBasics.ts, 5, 25)) +>E1.ExistingShorthand : Symbol(E1.ExistingShorthand, Decl(enumLiteralBasics.ts, 5, 25)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) +>ExistingShorthand : Symbol(E1.ExistingShorthand, Decl(enumLiteralBasics.ts, 5, 25)) + +const p_string: E1.String = E1.String; // ok +>p_string : Symbol(p_string, Decl(enumLiteralBasics.ts, 20, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) +>String : Symbol(E1.String, Decl(enumLiteralBasics.ts, 7, 11)) +>E1.String : Symbol(E1.String, Decl(enumLiteralBasics.ts, 7, 11)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) +>String : Symbol(E1.String, Decl(enumLiteralBasics.ts, 7, 11)) + +p_int = E1.Flag; // Type 'E1.Flag' is not assignable to type 'E1.Int'. +>p_int : Symbol(p_int, Decl(enumLiteralBasics.ts, 17, 3)) +>E1.Flag : Symbol(E1.Flag, Decl(enumLiteralBasics.ts, 8, 21)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) +>Flag : Symbol(E1.Flag, Decl(enumLiteralBasics.ts, 8, 21)) + +p_int = E1.Int | E1.Flag; +>p_int : Symbol(p_int, Decl(enumLiteralBasics.ts, 17, 3)) +>E1.Int : Symbol(E1.Int, Decl(enumLiteralBasics.ts, 6, 22)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) +>Int : Symbol(E1.Int, Decl(enumLiteralBasics.ts, 6, 22)) +>E1.Flag : Symbol(E1.Flag, Decl(enumLiteralBasics.ts, 8, 21)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) +>Flag : Symbol(E1.Flag, Decl(enumLiteralBasics.ts, 8, 21)) + // Assigning values which are not part of the enum despite being present in the enum const nonexist_bad: E1 = undefined; // error ->nonexist_bad : Symbol(nonexist_bad, Decl(enumLiteralBasics.ts, 19, 5)) ->E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) +>nonexist_bad : Symbol(nonexist_bad, Decl(enumLiteralBasics.ts, 25, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) >undefined : Symbol(undefined) const exist_bad: E1 = "exists"; // error ->exist_bad : Symbol(exist_bad, Decl(enumLiteralBasics.ts, 20, 5)) ->E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) +>exist_bad : Symbol(exist_bad, Decl(enumLiteralBasics.ts, 26, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) const ival_good: E1 = 1; // ok -- TypeScript is permissive of this in enums, to permit things like bitwise combinations of enum values. ->ival_good : Symbol(ival_good, Decl(enumLiteralBasics.ts, 21, 5)) ->E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) +>ival_good : Symbol(ival_good, Decl(enumLiteralBasics.ts, 27, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) const sval_bad: E1 = "string"; // error ->sval_bad : Symbol(sval_bad, Decl(enumLiteralBasics.ts, 22, 5)) ->E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) +>sval_bad : Symbol(sval_bad, Decl(enumLiteralBasics.ts, 28, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) const ival_notpresent: E1 = 4; // ok -- TypeScript is permissive of this in enums, to permit things like bitwise combinations of enum values. ->ival_notpresent : Symbol(ival_notpresent, Decl(enumLiteralBasics.ts, 24, 5)) ->E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) +>ival_notpresent : Symbol(ival_notpresent, Decl(enumLiteralBasics.ts, 30, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) function functest(value: E1) { ->functest : Symbol(functest, Decl(enumLiteralBasics.ts, 24, 30)) ->value : Symbol(value, Decl(enumLiteralBasics.ts, 26, 18)) ->E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) +>functest : Symbol(functest, Decl(enumLiteralBasics.ts, 30, 30)) +>value : Symbol(value, Decl(enumLiteralBasics.ts, 32, 18)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) console.log(value); >console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) >console : Symbol(console, Decl(lib.dom.d.ts, --, --)) >log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) ->value : Symbol(value, Decl(enumLiteralBasics.ts, 26, 18)) +>value : Symbol(value, Decl(enumLiteralBasics.ts, 32, 18)) return value; ->value : Symbol(value, Decl(enumLiteralBasics.ts, 26, 18)) +>value : Symbol(value, Decl(enumLiteralBasics.ts, 32, 18)) } const nonexist_bad2: E1 = functest(undefined); // error ->nonexist_bad2 : Symbol(nonexist_bad2, Decl(enumLiteralBasics.ts, 31, 5)) ->E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) ->functest : Symbol(functest, Decl(enumLiteralBasics.ts, 24, 30)) +>nonexist_bad2 : Symbol(nonexist_bad2, Decl(enumLiteralBasics.ts, 37, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) +>functest : Symbol(functest, Decl(enumLiteralBasics.ts, 30, 30)) >undefined : Symbol(undefined) const exist_bad2: E1 = functest("exists"); // error ->exist_bad2 : Symbol(exist_bad2, Decl(enumLiteralBasics.ts, 32, 5)) ->E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) ->functest : Symbol(functest, Decl(enumLiteralBasics.ts, 24, 30)) +>exist_bad2 : Symbol(exist_bad2, Decl(enumLiteralBasics.ts, 38, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) +>functest : Symbol(functest, Decl(enumLiteralBasics.ts, 30, 30)) const ival_good2: E1 = functest(1); // ok ->ival_good2 : Symbol(ival_good2, Decl(enumLiteralBasics.ts, 33, 5)) ->E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) ->functest : Symbol(functest, Decl(enumLiteralBasics.ts, 24, 30)) +>ival_good2 : Symbol(ival_good2, Decl(enumLiteralBasics.ts, 39, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) +>functest : Symbol(functest, Decl(enumLiteralBasics.ts, 30, 30)) const ival_good3: E1 = functest(4); // ok ->ival_good3 : Symbol(ival_good3, Decl(enumLiteralBasics.ts, 34, 5)) ->E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) ->functest : Symbol(functest, Decl(enumLiteralBasics.ts, 24, 30)) +>ival_good3 : Symbol(ival_good3, Decl(enumLiteralBasics.ts, 40, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) +>functest : Symbol(functest, Decl(enumLiteralBasics.ts, 30, 30)) const ival_good4: E1 = functest(E1.Int | E1.Flag); // ok ->ival_good4 : Symbol(ival_good4, Decl(enumLiteralBasics.ts, 35, 5)) ->E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) ->functest : Symbol(functest, Decl(enumLiteralBasics.ts, 24, 30)) +>ival_good4 : Symbol(ival_good4, Decl(enumLiteralBasics.ts, 41, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) +>functest : Symbol(functest, Decl(enumLiteralBasics.ts, 30, 30)) >E1.Int : Symbol(E1.Int, Decl(enumLiteralBasics.ts, 6, 22)) ->E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) >Int : Symbol(E1.Int, Decl(enumLiteralBasics.ts, 6, 22)) >E1.Flag : Symbol(E1.Flag, Decl(enumLiteralBasics.ts, 8, 21)) ->E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) >Flag : Symbol(E1.Flag, Decl(enumLiteralBasics.ts, 8, 21)) const sval_good2: E1 = functest(E1.String); ->sval_good2 : Symbol(sval_good2, Decl(enumLiteralBasics.ts, 36, 5)) ->E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) ->functest : Symbol(functest, Decl(enumLiteralBasics.ts, 24, 30)) +>sval_good2 : Symbol(sval_good2, Decl(enumLiteralBasics.ts, 42, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) +>functest : Symbol(functest, Decl(enumLiteralBasics.ts, 30, 30)) >E1.String : Symbol(E1.String, Decl(enumLiteralBasics.ts, 7, 11)) ->E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) >String : Symbol(E1.String, Decl(enumLiteralBasics.ts, 7, 11)) const sval_bad2: E1 = functest("string"); // error ->sval_bad2 : Symbol(sval_bad2, Decl(enumLiteralBasics.ts, 37, 5)) ->E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 5)) ->functest : Symbol(functest, Decl(enumLiteralBasics.ts, 24, 30)) +>sval_bad2 : Symbol(sval_bad2, Decl(enumLiteralBasics.ts, 43, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) +>functest : Symbol(functest, Decl(enumLiteralBasics.ts, 30, 30)) + +function functest2(value: E1.Int) { +>functest2 : Symbol(functest2, Decl(enumLiteralBasics.ts, 43, 41)) +>value : Symbol(value, Decl(enumLiteralBasics.ts, 45, 19)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) +>Int : Symbol(E1.Int, Decl(enumLiteralBasics.ts, 6, 22)) + + console.log(value); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>value : Symbol(value, Decl(enumLiteralBasics.ts, 45, 19)) + + return value; +>value : Symbol(value, Decl(enumLiteralBasics.ts, 45, 19)) +} + +const nonexist_bad3: E1.Int = functest2(undefined); +>nonexist_bad3 : Symbol(nonexist_bad3, Decl(enumLiteralBasics.ts, 50, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) +>Int : Symbol(E1.Int, Decl(enumLiteralBasics.ts, 6, 22)) +>functest2 : Symbol(functest2, Decl(enumLiteralBasics.ts, 43, 41)) +>undefined : Symbol(undefined) + +const exist_bad3: E1.Int = functest2("exists"); // error +>exist_bad3 : Symbol(exist_bad3, Decl(enumLiteralBasics.ts, 51, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) +>Int : Symbol(E1.Int, Decl(enumLiteralBasics.ts, 6, 22)) +>functest2 : Symbol(functest2, Decl(enumLiteralBasics.ts, 43, 41)) + +const ival_good5: E1.Int = functest2(1); // ok +>ival_good5 : Symbol(ival_good5, Decl(enumLiteralBasics.ts, 52, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) +>Int : Symbol(E1.Int, Decl(enumLiteralBasics.ts, 6, 22)) +>functest2 : Symbol(functest2, Decl(enumLiteralBasics.ts, 43, 41)) + +const ival_good6: E1.Int = functest2(4); // ok +>ival_good6 : Symbol(ival_good6, Decl(enumLiteralBasics.ts, 53, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) +>Int : Symbol(E1.Int, Decl(enumLiteralBasics.ts, 6, 22)) +>functest2 : Symbol(functest2, Decl(enumLiteralBasics.ts, 43, 41)) + +const ival_good7: E1.Int = functest2(E1.Int | E1.Flag); // ok +>ival_good7 : Symbol(ival_good7, Decl(enumLiteralBasics.ts, 54, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) +>Int : Symbol(E1.Int, Decl(enumLiteralBasics.ts, 6, 22)) +>functest2 : Symbol(functest2, Decl(enumLiteralBasics.ts, 43, 41)) +>E1.Int : Symbol(E1.Int, Decl(enumLiteralBasics.ts, 6, 22)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) +>Int : Symbol(E1.Int, Decl(enumLiteralBasics.ts, 6, 22)) +>E1.Flag : Symbol(E1.Flag, Decl(enumLiteralBasics.ts, 8, 21)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) +>Flag : Symbol(E1.Flag, Decl(enumLiteralBasics.ts, 8, 21)) + +const sval_good3: E1.Int = functest2(E1.String); +>sval_good3 : Symbol(sval_good3, Decl(enumLiteralBasics.ts, 55, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) +>Int : Symbol(E1.Int, Decl(enumLiteralBasics.ts, 6, 22)) +>functest2 : Symbol(functest2, Decl(enumLiteralBasics.ts, 43, 41)) +>E1.String : Symbol(E1.String, Decl(enumLiteralBasics.ts, 7, 11)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) +>String : Symbol(E1.String, Decl(enumLiteralBasics.ts, 7, 11)) + +const sval_bad3: E1.Flag = functest2("string"); // error +>sval_bad3 : Symbol(sval_bad3, Decl(enumLiteralBasics.ts, 56, 5)) +>E1 : Symbol(E1, Decl(enumLiteralBasics.ts, 4, 5), Decl(enumLiteralBasics.ts, 4, 14)) +>Flag : Symbol(E1.Flag, Decl(enumLiteralBasics.ts, 8, 21)) +>functest2 : Symbol(functest2, Decl(enumLiteralBasics.ts, 43, 41)) diff --git a/tests/baselines/reference/enumLiteralBasics.types b/tests/baselines/reference/enumLiteralBasics.types index 8640f5138018b..9048af49836d3 100644 --- a/tests/baselines/reference/enumLiteralBasics.types +++ b/tests/baselines/reference/enumLiteralBasics.types @@ -12,8 +12,6 @@ let ExistingShorthand = "exists"; const E1: enum = { >E1 : E1 -> : ^^ ->E1 : E1 > : ^^ NonexistingShorthand, // error -- EnumLiteralExpressions require explicit property definitions. @@ -85,6 +83,86 @@ const sval: E1 = E1.String; // ok >String : E1.String > : ^^^^^^^^^ +let p_int: E1.Int = E1.Int; // ok +>p_int : E1.Int +> : ^^^^^^ +>E1 : any +> : ^^^ +>E1.Int : E1.Int +> : ^^^^^^ +>E1 : typeof E1 +> : ^^^^^^^^^ +>Int : E1.Int +> : ^^^^^^ + +const p_nonexist: E1.NonexistingShorthand = E1.NonexistingShorthand; // ok +>p_nonexist : E1.NonexistingShorthand +> : ^^^^^^^^^^^^^^^^^^^^^^^ +>E1 : any +> : ^^^ +>E1.NonexistingShorthand : E1.NonexistingShorthand +> : ^^^^^^^^^^^^^^^^^^^^^^^ +>E1 : typeof E1 +> : ^^^^^^^^^ +>NonexistingShorthand : E1.NonexistingShorthand +> : ^^^^^^^^^^^^^^^^^^^^^^^ + +const p_exist: E1.ExistingShorthand = E1.ExistingShorthand; // ok +>p_exist : E1.ExistingShorthand +> : ^^^^^^^^^^^^^^^^^^^^ +>E1 : any +> : ^^^ +>E1.ExistingShorthand : E1.ExistingShorthand +> : ^^^^^^^^^^^^^^^^^^^^ +>E1 : typeof E1 +> : ^^^^^^^^^ +>ExistingShorthand : E1.ExistingShorthand +> : ^^^^^^^^^^^^^^^^^^^^ + +const p_string: E1.String = E1.String; // ok +>p_string : E1.String +> : ^^^^^^^^^ +>E1 : any +> : ^^^ +>E1.String : E1.String +> : ^^^^^^^^^ +>E1 : typeof E1 +> : ^^^^^^^^^ +>String : E1.String +> : ^^^^^^^^^ + +p_int = E1.Flag; // Type 'E1.Flag' is not assignable to type 'E1.Int'. +>p_int = E1.Flag : E1.Flag +> : ^^^^^^^ +>p_int : E1.Int +> : ^^^^^^ +>E1.Flag : E1.Flag +> : ^^^^^^^ +>E1 : typeof E1 +> : ^^^^^^^^^ +>Flag : E1.Flag +> : ^^^^^^^ + +p_int = E1.Int | E1.Flag; +>p_int = E1.Int | E1.Flag : number +> : ^^^^^^ +>p_int : E1.Int +> : ^^^^^^ +>E1.Int | E1.Flag : number +> : ^^^^^^ +>E1.Int : E1.Int +> : ^^^^^^ +>E1 : typeof E1 +> : ^^^^^^^^^ +>Int : E1.Int +> : ^^^^^^ +>E1.Flag : E1.Flag +> : ^^^^^^^ +>E1 : typeof E1 +> : ^^^^^^^^^ +>Flag : E1.Flag +> : ^^^^^^^ + // Assigning values which are not part of the enum despite being present in the enum const nonexist_bad: E1 = undefined; // error >nonexist_bad : E1 @@ -225,3 +303,128 @@ const sval_bad2: E1 = functest("string"); // error >"string" : "string" > : ^^^^^^^^ +function functest2(value: E1.Int) { +>functest2 : (value: E1.Int) => E1.Int +> : ^ ^^^^^^^^^^^^^^^^^^^ +>value : E1.Int +> : ^^^^^^ +>E1 : any +> : ^^^ + + console.log(value); +>console.log(value) : void +> : ^^^^ +>console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>console : Console +> : ^^^^^^^ +>log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>value : E1.Int +> : ^^^^^^ + + return value; +>value : E1.Int +> : ^^^^^^ +} + +const nonexist_bad3: E1.Int = functest2(undefined); +>nonexist_bad3 : E1.Int +> : ^^^^^^ +>E1 : any +> : ^^^ +>functest2(undefined) : E1.Int +> : ^^^^^^ +>functest2 : (value: E1.Int) => E1.Int +> : ^ ^^^^^^^^^^^^^^^^^^^ +>undefined : undefined +> : ^^^^^^^^^ + +const exist_bad3: E1.Int = functest2("exists"); // error +>exist_bad3 : E1.Int +> : ^^^^^^ +>E1 : any +> : ^^^ +>functest2("exists") : E1.Int +> : ^^^^^^ +>functest2 : (value: E1.Int) => E1.Int +> : ^ ^^^^^^^^^^^^^^^^^^^ +>"exists" : "exists" +> : ^^^^^^^^ + +const ival_good5: E1.Int = functest2(1); // ok +>ival_good5 : E1.Int +> : ^^^^^^ +>E1 : any +> : ^^^ +>functest2(1) : E1.Int +> : ^^^^^^ +>functest2 : (value: E1.Int) => E1.Int +> : ^ ^^^^^^^^^^^^^^^^^^^ +>1 : 1 +> : ^ + +const ival_good6: E1.Int = functest2(4); // ok +>ival_good6 : E1.Int +> : ^^^^^^ +>E1 : any +> : ^^^ +>functest2(4) : E1.Int +> : ^^^^^^ +>functest2 : (value: E1.Int) => E1.Int +> : ^ ^^^^^^^^^^^^^^^^^^^ +>4 : 4 +> : ^ + +const ival_good7: E1.Int = functest2(E1.Int | E1.Flag); // ok +>ival_good7 : E1.Int +> : ^^^^^^ +>E1 : any +> : ^^^ +>functest2(E1.Int | E1.Flag) : E1.Int +> : ^^^^^^ +>functest2 : (value: E1.Int) => E1.Int +> : ^ ^^^^^^^^^^^^^^^^^^^ +>E1.Int | E1.Flag : number +> : ^^^^^^ +>E1.Int : E1.Int +> : ^^^^^^ +>E1 : typeof E1 +> : ^^^^^^^^^ +>Int : E1.Int +> : ^^^^^^ +>E1.Flag : E1.Flag +> : ^^^^^^^ +>E1 : typeof E1 +> : ^^^^^^^^^ +>Flag : E1.Flag +> : ^^^^^^^ + +const sval_good3: E1.Int = functest2(E1.String); +>sval_good3 : E1.Int +> : ^^^^^^ +>E1 : any +> : ^^^ +>functest2(E1.String) : E1.Int +> : ^^^^^^ +>functest2 : (value: E1.Int) => E1.Int +> : ^ ^^^^^^^^^^^^^^^^^^^ +>E1.String : E1.String +> : ^^^^^^^^^ +>E1 : typeof E1 +> : ^^^^^^^^^ +>String : E1.String +> : ^^^^^^^^^ + +const sval_bad3: E1.Flag = functest2("string"); // error +>sval_bad3 : E1.Flag +> : ^^^^^^^ +>E1 : any +> : ^^^ +>functest2("string") : E1.Int +> : ^^^^^^ +>functest2 : (value: E1.Int) => E1.Int +> : ^ ^^^^^^^^^^^^^^^^^^^ +>"string" : "string" +> : ^^^^^^^^ + diff --git a/tests/baselines/reference/enumLiteralSimple.symbols b/tests/baselines/reference/enumLiteralSimple.symbols index 4784ea41a16dc..75a6792a56513 100644 --- a/tests/baselines/reference/enumLiteralSimple.symbols +++ b/tests/baselines/reference/enumLiteralSimple.symbols @@ -2,8 +2,7 @@ === enumLiteralSimple.ts === const ENUM: enum = { ->ENUM : Symbol(ENUM, Decl(enumLiteralSimple.ts, 0, 5), Decl(enumLiteralSimple.ts, 0, 5)) ->ENUM : Symbol(ENUM, Decl(enumLiteralSimple.ts, 0, 5), Decl(enumLiteralSimple.ts, 0, 5)) +>ENUM : Symbol(ENUM, Decl(enumLiteralSimple.ts, 0, 5), Decl(enumLiteralSimple.ts, 0, 16)) a: 1, >a : Symbol(ENUM.a, Decl(enumLiteralSimple.ts, 0, 20)) @@ -15,5 +14,5 @@ const ENUM: enum = { const a: ENUM = 1 >a : Symbol(a, Decl(enumLiteralSimple.ts, 5, 5)) ->ENUM : Symbol(ENUM, Decl(enumLiteralSimple.ts, 0, 5), Decl(enumLiteralSimple.ts, 0, 5)) +>ENUM : Symbol(ENUM, Decl(enumLiteralSimple.ts, 0, 5), Decl(enumLiteralSimple.ts, 0, 16)) diff --git a/tests/baselines/reference/enumLiteralSimple.types b/tests/baselines/reference/enumLiteralSimple.types index b1be509828138..b33c8a58ffad3 100644 --- a/tests/baselines/reference/enumLiteralSimple.types +++ b/tests/baselines/reference/enumLiteralSimple.types @@ -3,8 +3,6 @@ === enumLiteralSimple.ts === const ENUM: enum = { >ENUM : ENUM -> : ^^^^ ->ENUM : ENUM > : ^^^^ a: 1, diff --git a/tests/cases/conformance/enums/enumLiteralBasics.ts b/tests/cases/conformance/enums/enumLiteralBasics.ts index 6bdcd19f2cf17..dae3fab52127d 100644 --- a/tests/cases/conformance/enums/enumLiteralBasics.ts +++ b/tests/cases/conformance/enums/enumLiteralBasics.ts @@ -15,6 +15,12 @@ const nonexist: E1 = E1.NonexistingShorthand; // ok const exist: E1 = E1.ExistingShorthand; // ok const ival: E1 = E1.Int; // ok const sval: E1 = E1.String; // ok +let p_int: E1.Int = E1.Int; // ok +const p_nonexist: E1.NonexistingShorthand = E1.NonexistingShorthand; // ok +const p_exist: E1.ExistingShorthand = E1.ExistingShorthand; // ok +const p_string: E1.String = E1.String; // ok +p_int = E1.Flag; // Type 'E1.Flag' is not assignable to type 'E1.Int'. +p_int = E1.Int | E1.Flag; // Assigning values which are not part of the enum despite being present in the enum const nonexist_bad: E1 = undefined; // error @@ -36,3 +42,16 @@ const ival_good3: E1 = functest(4); // ok const ival_good4: E1 = functest(E1.Int | E1.Flag); // ok const sval_good2: E1 = functest(E1.String); const sval_bad2: E1 = functest("string"); // error + +function functest2(value: E1.Int) { + console.log(value); + return value; +} + +const nonexist_bad3: E1.Int = functest2(undefined); +const exist_bad3: E1.Int = functest2("exists"); // error +const ival_good5: E1.Int = functest2(1); // ok +const ival_good6: E1.Int = functest2(4); // ok +const ival_good7: E1.Int = functest2(E1.Int | E1.Flag); // ok +const sval_good3: E1.Int = functest2(E1.String); +const sval_bad3: E1.Flag = functest2("string"); // error \ No newline at end of file From d40b1d0900095c96359d2390b30b75c347eb973b Mon Sep 17 00:00:00 2001 From: Caitlin Potter Date: Sat, 15 Mar 2025 00:57:33 -0400 Subject: [PATCH 11/14] EnumLiteralExpression name is no longer an AST node, bunch of linter errors are fixed --- src/compiler/binder.ts | 2 -- src/compiler/checker.ts | 3 ++- src/compiler/parser.ts | 5 +---- src/compiler/transformers/ts.ts | 2 -- src/compiler/utilities.ts | 2 -- src/compiler/visitorPublic.ts | 4 ++-- tests/baselines/reference/api/typescript.d.ts | 6 +++--- 7 files changed, 8 insertions(+), 16 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index d3dadfd5a24d9..7ac2b9c17f25e 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -85,7 +85,6 @@ import { FunctionLikeDeclaration, GetAccessorDeclaration, getAssignedExpandoInitializer, - getAssignedName, getAssignmentDeclarationKind, getAssignmentDeclarationPropertyAccessKind, getCombinedModifierFlags, @@ -158,7 +157,6 @@ import { isEntityNameExpression, isEnumConst, isEnumLiteralExpression, - isEnumTypeReference, isExportAssignment, isExportDeclaration, isExportsIdentifier, diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 89639e504bc4c..83b8423de8857 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -47104,6 +47104,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const isConstEnum = isEnumConst(member.parent); const initializer = member.initializer!; const result = evaluate(initializer, member); + const isDecl = isEnumDeclaration(member.parent); if (result.value !== undefined) { if (isConstEnum && typeof result.value === "number" && !isFinite(result.value)) { error( @@ -47117,7 +47118,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { 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)}`, + isDecl ? `${idText(member.parent.name)}.${getTextOfPropertyName(member.name)}` : `${member.parent.name}.${getTextOfPropertyName(member.name)}`, ); } } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index dd476584436c8..a0c5622a17e67 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -110,7 +110,6 @@ import { getLeadingCommentRanges, getSpellingSuggestion, getTextOfNodeFromSourceText, - getTokenPosOfNode, HasJSDoc, hasJSDocNodes, HasModifiers, @@ -330,7 +329,6 @@ import { ScriptKind, ScriptTarget, SetAccessorDeclaration, - setOriginalNode, setParent, setParentRecursive, setTextRange, @@ -7097,7 +7095,7 @@ namespace Parser { let variableDeclaration; if (parseOptional(SyntaxKind.OpenParenToken)) { - variableDeclaration = parseVariableDeclaration() as VariableDeclaration; + variableDeclaration = parseVariableDeclaration(); parseExpected(SyntaxKind.CloseParenToken); } else { @@ -8268,7 +8266,6 @@ namespace Parser { // const MyEnum: enum = { name: value } const pos = getNodePos(); const hasJSDoc = hasPrecedingJSDocComment(); - const tokenIsIdentifier = isIdentifier(); const name = parsePropertyName(); let initializer: Expression | undefined; diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 12a06b8cb9284..47d6e38c5184d 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -86,7 +86,6 @@ import { isElementAccessExpression, isEntityName, isEnumConst, - isEnumMember, isExportAssignment, isExportDeclaration, isExportOrDefaultModifier, @@ -170,7 +169,6 @@ import { setInternalEmitFlags, setOriginalNode, setParent, - setParentRecursive, setSourceMapRange, setSyntheticLeadingComments, setSyntheticTrailingComments, diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 7f5ddd6b2f951..f4d07aad6bc67 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -277,9 +277,7 @@ import { isElementAccessExpression, isEnumDeclaration, isEnumLiteralDeclaration, - isEnumLiteralExpression, isEnumMember, - isEnumTypeReference, isExportAssignment, isExportDeclaration, isExpressionStatement, diff --git a/src/compiler/visitorPublic.ts b/src/compiler/visitorPublic.ts index 4f15d1452899e..157b83090a21c 100644 --- a/src/compiler/visitorPublic.ts +++ b/src/compiler/visitorPublic.ts @@ -1480,11 +1480,11 @@ const visitEachChildTable: VisitEachChildTable = { ); }, - [SyntaxKind.EnumLiteralExpression]: function visitEachChildOfEnumDeclaration(node, visitor, context, nodesVisitor, nodeVisitor, _tokenVisitor) { + [SyntaxKind.EnumLiteralExpression]: function visitEachChildOfEnumDeclaration(node, visitor, context, nodesVisitor, _tokenVisitor) { return context.factory.updateEnumLiteralExpression( node, nodesVisitor(node.modifiers, visitor, isModifierLike), - Debug.checkDefined(nodeVisitor(node.name, visitor, isIdentifier)), + node.name, nodesVisitor(node.members, visitor, isEnumMember), ); }, diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 8b58efb21cff1..6cc1e4c5758c8 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -5454,7 +5454,7 @@ declare namespace ts { readonly kind: SyntaxKind.EnumLiteralExpression; readonly parent: VariableDeclaration; readonly modifiers?: NodeArray; - readonly name: Identifier; + readonly name: __String; readonly members: NodeArray; } type EnumDeclarationType = EnumDeclaration | EnumLiteralExpression; @@ -7711,8 +7711,8 @@ declare namespace ts { updateTypeAliasDeclaration(node: TypeAliasDeclaration, modifiers: readonly ModifierLike[] | undefined, name: Identifier, typeParameters: readonly TypeParameterDeclaration[] | undefined, type: TypeNode): TypeAliasDeclaration; createEnumDeclaration(modifiers: readonly ModifierLike[] | undefined, name: string | Identifier, members: readonly EnumMember[]): EnumDeclaration; updateEnumDeclaration(node: EnumDeclaration, modifiers: readonly ModifierLike[] | undefined, name: Identifier, members: readonly EnumMember[]): EnumDeclaration; - createEnumLiteralExpression(modifiers: readonly ModifierLike[] | undefined, name: string | Identifier, members: readonly EnumMember[]): EnumLiteralExpression; - updateEnumLiteralExpression(node: EnumLiteralExpression, modifiers: readonly ModifierLike[] | undefined, name: Identifier, members: readonly EnumMember[]): EnumLiteralExpression; + createEnumLiteralExpression(modifiers: readonly ModifierLike[] | undefined, name: __String, members: readonly EnumMember[]): EnumLiteralExpression; + updateEnumLiteralExpression(node: EnumLiteralExpression, modifiers: readonly ModifierLike[] | undefined, name: __String, members: readonly EnumMember[]): EnumLiteralExpression; createModuleDeclaration(modifiers: readonly ModifierLike[] | undefined, name: ModuleName, body: ModuleBody | undefined, flags?: NodeFlags): ModuleDeclaration; updateModuleDeclaration(node: ModuleDeclaration, modifiers: readonly ModifierLike[] | undefined, name: ModuleName, body: ModuleBody | undefined): ModuleDeclaration; createModuleBlock(statements: readonly Statement[]): ModuleBlock; From 380c082605dca5413415c1ecfb7f23a16453acc0 Mon Sep 17 00:00:00 2001 From: Caitlin Potter Date: Tue, 18 Mar 2025 10:26:54 -0400 Subject: [PATCH 12/14] Remove empty branch --- src/compiler/binder.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 7ac2b9c17f25e..2da76df3ac856 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -156,7 +156,6 @@ import { isEmptyObjectLiteral, isEntityNameExpression, isEnumConst, - isEnumLiteralExpression, isExportAssignment, isExportDeclaration, isExportsIdentifier, @@ -824,9 +823,6 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void { if (isNamedDeclaration(node)) { setParent(node.name, node); } - if (isEnumLiteralExpression(node) && symbol.valueDeclaration === node.parent) { - // This is not a real redeclaration, but is in fact two separate meanings for the same symbol. - } // Report errors every position with duplicate declaration // Report errors on previous encountered declarations let message = symbol.flags & SymbolFlags.BlockScopedVariable From 34312afd3c7213f9df6c0e9af335be3c7fd8b8b3 Mon Sep 17 00:00:00 2001 From: Caitlin Potter Date: Tue, 18 Mar 2025 10:41:39 -0400 Subject: [PATCH 13/14] attempt to make getTypeFromTypeReference changes easier to read but it adds a bunch of casting, which isn't great --- src/compiler/checker.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 83b8423de8857..654563b038456 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -541,7 +541,6 @@ import { isEnumLiteralExpression, isEnumMember, isEnumTypeAnnotation, - isEnumTypeReference, isExclusivelyTypeOnlyImportOrExport, isExpandoPropertyDeclaration, isExportAssignment, @@ -16712,8 +16711,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return links.resolvedType = checkExpressionCached(node.parent.expression); } // `var MyEnum: enum = { FirstValue: 1, SecondValue: 2 }` should resolve to a union of the enum values. - if (isEnumTypeReference(node) && isVariableDeclaration(node.parent) && node.parent.initializer && isEnumLiteralExpression(node.parent.initializer)) { - return links.resolvedType = checkExpressionCached(node.parent.initializer); + if (node.parent && isEnumLiteralDeclaration(node.parent)) { + return links.resolvedType = checkExpressionCached((node.parent as VariableDeclaration).initializer as Expression); } let symbol: Symbol | undefined; let type: Type | undefined; @@ -44140,7 +44139,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { checkClassNameCollisionWithObject(name); } } - else if (isEnumDeclaration(node) || (isVariableDeclaration(node) && node.initializer && isEnumLiteralExpression(node.initializer))) { + else if (isEnumDeclaration(node) || isEnumLiteralDeclaration(node)) { checkTypeNameIsReserved(name, Diagnostics.Enum_name_cannot_be_0); } } From 7a397ea32b989ced59f995ee5ed7b554f97ce31f Mon Sep 17 00:00:00 2001 From: Caitlin Potter Date: Tue, 18 Mar 2025 10:27:54 -0400 Subject: [PATCH 14/14] Refactor isEnumLiteralDeclaration() to allow assumptions about members --- src/compiler/checker.ts | 2 +- src/compiler/types.ts | 8 ++++++++ src/compiler/utilitiesPublic.ts | 3 ++- tests/baselines/reference/api/typescript.d.ts | 9 ++++++++- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 654563b038456..c56909ab44a29 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -16712,7 +16712,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } // `var MyEnum: enum = { FirstValue: 1, SecondValue: 2 }` should resolve to a union of the enum values. if (node.parent && isEnumLiteralDeclaration(node.parent)) { - return links.resolvedType = checkExpressionCached((node.parent as VariableDeclaration).initializer as Expression); + return links.resolvedType = checkExpressionCached(node.parent.initializer); } let symbol: Symbol | undefined; let type: Type | undefined; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index a6415069d8e2a..1f11e1b7be599 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1883,6 +1883,14 @@ export interface VariableDeclaration extends NamedDeclaration, JSDocContainer { readonly initializer?: Expression; // Optional initializer } +export interface EnumLiteralDeclaration extends VariableDeclaration { + readonly kind: SyntaxKind.VariableDeclaration; + readonly parent: VariableDeclarationList; + readonly name: BindingName; + readonly type: TypeReferenceNode; + readonly initializer: EnumLiteralExpression; +} + /** @internal */ export type InitializedVariableDeclaration = VariableDeclaration & { readonly initializer: Expression; }; diff --git a/src/compiler/utilitiesPublic.ts b/src/compiler/utilitiesPublic.ts index 0e7bc175614cc..75a80ecb88d8d 100644 --- a/src/compiler/utilitiesPublic.ts +++ b/src/compiler/utilitiesPublic.ts @@ -56,6 +56,7 @@ import { EntityName, entityNameToString, EnumDeclaration, + EnumLiteralDeclaration, every, ExportAssignment, ExportDeclaration, @@ -1431,7 +1432,7 @@ export function isEnumTypeReference(node: Node): boolean { return isTypeReferenceNode(node) && isIdentifier(node.typeName) && node.typeName.escapedText === "enum" && !node.typeArguments; } -export function isEnumLiteralDeclaration(node: Node): boolean { +export function isEnumLiteralDeclaration(node: Node): node is EnumLiteralDeclaration { return isVariableDeclaration(node) && hasType(node) && isEnumTypeReference(node.type!) && hasInitializer(node) && isEnumLiteralExpression(node.initializer!); } diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 6cc1e4c5758c8..512e4bbcbb664 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -4529,6 +4529,13 @@ declare namespace ts { readonly type?: TypeNode; readonly initializer?: Expression; } + interface EnumLiteralDeclaration extends VariableDeclaration { + readonly kind: SyntaxKind.VariableDeclaration; + readonly parent: VariableDeclarationList; + readonly name: BindingName; + readonly type: TypeReferenceNode; + readonly initializer: EnumLiteralExpression; + } interface VariableDeclarationList extends Node { readonly kind: SyntaxKind.VariableDeclarationList; readonly parent: VariableStatement | ForStatement | ForOfStatement | ForInStatement; @@ -8740,7 +8747,7 @@ declare namespace ts { function isNullishCoalesce(node: Node): boolean; function isConstTypeReference(node: Node): boolean; function isEnumTypeReference(node: Node): boolean; - function isEnumLiteralDeclaration(node: Node): boolean; + function isEnumLiteralDeclaration(node: Node): node is EnumLiteralDeclaration; function isEnumTypeAnnotation(node: Node): boolean; function skipPartiallyEmittedExpressions(node: Expression): Expression; function skipPartiallyEmittedExpressions(node: Node): Node;