diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2578ba4c4df63..4d446b6d0feee 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -46726,17 +46726,19 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } if (isConstantVariable(symbol)) { const declaration = symbol.valueDeclaration; - if (declaration && isVariableDeclaration(declaration) && !declaration.type && declaration.initializer && (!location || declaration !== location && isBlockScopedNameDeclaredBeforeUse(declaration, location))) { - const result = evaluate(declaration.initializer, declaration); - if (location && getSourceFileOfNode(location) !== getSourceFileOfNode(declaration)) { - return evaluatorResult( - result.value, - /*isSyntacticallyString*/ false, - /*resolvedOtherFiles*/ true, - /*hasExternalReferences*/ true, - ); + if (declaration && isVariableDeclaration(declaration)) { + if (!declaration.type && declaration.initializer && (!location || declaration !== location && isBlockScopedNameDeclaredBeforeUse(declaration, location))) { + const result = evaluate(declaration.initializer, declaration); + return location && getSourceFileOfNode(location) !== getSourceFileOfNode(declaration) ? + evaluatorResult(result.value, /*isSyntacticallyString*/ false, /*resolvedOtherFiles*/ true, /*hasExternalReferences*/ true) : + evaluatorResult(result.value, result.isSyntacticallyString, result.resolvedOtherFiles, /*hasExternalReferences*/ true); + } + if (declaration.type && declaration.type.kind === SyntaxKind.LiteralType) { + const type = getTypeOfSymbol(symbol); + if (type.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral)) { + return evaluatorResult((type as LiteralType).value as string | number); + } } - return evaluatorResult(result.value, result.isSyntacticallyString, result.resolvedOtherFiles, /*hasExternalReferences*/ true); } } return evaluatorResult(/*value*/ undefined); diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index a1ef5bb08d8e3..01f9bb4b0eff9 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -11110,6 +11110,11 @@ export function createEvaluator({ evaluateElementAccessExpression, evaluateEntit break; case SyntaxKind.ElementAccessExpression: return evaluateElementAccessExpression(expr as ElementAccessExpression, location); + case SyntaxKind.AsExpression: + if (isConstTypeReference((expr as AsExpression).type)) { + return evaluate((expr as AsExpression).expression, location); + } + break; } return evaluatorResult(/*value*/ undefined, isSyntacticallyString, resolvedOtherFiles, hasExternalReferences); } diff --git a/tests/baselines/reference/constantExpressions1.errors.txt b/tests/baselines/reference/constantExpressions1.errors.txt new file mode 100644 index 0000000000000..5ca3c584e73e0 --- /dev/null +++ b/tests/baselines/reference/constantExpressions1.errors.txt @@ -0,0 +1,41 @@ +constantExpressions1.ts(29,9): error TS2474: const enum member initializers must be constant expressions. +constantExpressions1.ts(30,9): error TS2474: const enum member initializers must be constant expressions. + + +==== constantExpressions1.ts (2 errors) ==== + const C00 = "a"; + const C01 = "b" as const; + const C02: "c" = "c"; + declare const C03: "d"; + + const enum E0 { + A = C00, + B = C01, + C = C02, + D = C03, + } + + const C10 = 1; + const C11 = 2 as const; + const C12: 3 = 3; + declare const C13: 4; + + const enum E1 { + A = C10, + B = C11, + C = C12, + D = C13, + } + + const C1: string = "x"; + const C2: "x" | "y" = "x"; + + const enum EE { + A = C1, // Error + ~~ +!!! error TS2474: const enum member initializers must be constant expressions. + B = C2, // Error + ~~ +!!! error TS2474: const enum member initializers must be constant expressions. + } + \ No newline at end of file diff --git a/tests/baselines/reference/constantExpressions1.js b/tests/baselines/reference/constantExpressions1.js new file mode 100644 index 0000000000000..ba5a4735f4b5e --- /dev/null +++ b/tests/baselines/reference/constantExpressions1.js @@ -0,0 +1,75 @@ +//// [tests/cases/compiler/constantExpressions1.ts] //// + +//// [constantExpressions1.ts] +const C00 = "a"; +const C01 = "b" as const; +const C02: "c" = "c"; +declare const C03: "d"; + +const enum E0 { + A = C00, + B = C01, + C = C02, + D = C03, +} + +const C10 = 1; +const C11 = 2 as const; +const C12: 3 = 3; +declare const C13: 4; + +const enum E1 { + A = C10, + B = C11, + C = C12, + D = C13, +} + +const C1: string = "x"; +const C2: "x" | "y" = "x"; + +const enum EE { + A = C1, // Error + B = C2, // Error +} + + +//// [constantExpressions1.js] +"use strict"; +var C00 = "a"; +var C01 = "b"; +var C02 = "c"; +var C10 = 1; +var C11 = 2; +var C12 = 3; +var C1 = "x"; +var C2 = "x"; + + +//// [constantExpressions1.d.ts] +declare const C00 = "a"; +declare const C01: "b"; +declare const C02: "c"; +declare const C03: "d"; +declare const enum E0 { + A = "a", + B = "b", + C = "c", + D = "d" +} +declare const C10 = 1; +declare const C11: 2; +declare const C12: 3; +declare const C13: 4; +declare const enum E1 { + A = 1, + B = 2, + C = 3, + D = 4 +} +declare const C1: string; +declare const C2: "x" | "y"; +declare const enum EE { + A,// Error + B +} diff --git a/tests/baselines/reference/constantExpressions1.symbols b/tests/baselines/reference/constantExpressions1.symbols new file mode 100644 index 0000000000000..a8b611272ccf1 --- /dev/null +++ b/tests/baselines/reference/constantExpressions1.symbols @@ -0,0 +1,87 @@ +//// [tests/cases/compiler/constantExpressions1.ts] //// + +=== constantExpressions1.ts === +const C00 = "a"; +>C00 : Symbol(C00, Decl(constantExpressions1.ts, 0, 5)) + +const C01 = "b" as const; +>C01 : Symbol(C01, Decl(constantExpressions1.ts, 1, 5)) +>const : Symbol(const) + +const C02: "c" = "c"; +>C02 : Symbol(C02, Decl(constantExpressions1.ts, 2, 5)) + +declare const C03: "d"; +>C03 : Symbol(C03, Decl(constantExpressions1.ts, 3, 13)) + +const enum E0 { +>E0 : Symbol(E0, Decl(constantExpressions1.ts, 3, 23)) + + A = C00, +>A : Symbol(E0.A, Decl(constantExpressions1.ts, 5, 15)) +>C00 : Symbol(C00, Decl(constantExpressions1.ts, 0, 5)) + + B = C01, +>B : Symbol(E0.B, Decl(constantExpressions1.ts, 6, 12)) +>C01 : Symbol(C01, Decl(constantExpressions1.ts, 1, 5)) + + C = C02, +>C : Symbol(E0.C, Decl(constantExpressions1.ts, 7, 12)) +>C02 : Symbol(C02, Decl(constantExpressions1.ts, 2, 5)) + + D = C03, +>D : Symbol(E0.D, Decl(constantExpressions1.ts, 8, 12)) +>C03 : Symbol(C03, Decl(constantExpressions1.ts, 3, 13)) +} + +const C10 = 1; +>C10 : Symbol(C10, Decl(constantExpressions1.ts, 12, 5)) + +const C11 = 2 as const; +>C11 : Symbol(C11, Decl(constantExpressions1.ts, 13, 5)) +>const : Symbol(const) + +const C12: 3 = 3; +>C12 : Symbol(C12, Decl(constantExpressions1.ts, 14, 5)) + +declare const C13: 4; +>C13 : Symbol(C13, Decl(constantExpressions1.ts, 15, 13)) + +const enum E1 { +>E1 : Symbol(E1, Decl(constantExpressions1.ts, 15, 21)) + + A = C10, +>A : Symbol(E1.A, Decl(constantExpressions1.ts, 17, 15)) +>C10 : Symbol(C10, Decl(constantExpressions1.ts, 12, 5)) + + B = C11, +>B : Symbol(E1.B, Decl(constantExpressions1.ts, 18, 12)) +>C11 : Symbol(C11, Decl(constantExpressions1.ts, 13, 5)) + + C = C12, +>C : Symbol(E1.C, Decl(constantExpressions1.ts, 19, 12)) +>C12 : Symbol(C12, Decl(constantExpressions1.ts, 14, 5)) + + D = C13, +>D : Symbol(E1.D, Decl(constantExpressions1.ts, 20, 12)) +>C13 : Symbol(C13, Decl(constantExpressions1.ts, 15, 13)) +} + +const C1: string = "x"; +>C1 : Symbol(C1, Decl(constantExpressions1.ts, 24, 5)) + +const C2: "x" | "y" = "x"; +>C2 : Symbol(C2, Decl(constantExpressions1.ts, 25, 5)) + +const enum EE { +>EE : Symbol(EE, Decl(constantExpressions1.ts, 25, 26)) + + A = C1, // Error +>A : Symbol(EE.A, Decl(constantExpressions1.ts, 27, 15)) +>C1 : Symbol(C1, Decl(constantExpressions1.ts, 24, 5)) + + B = C2, // Error +>B : Symbol(EE.B, Decl(constantExpressions1.ts, 28, 11)) +>C2 : Symbol(C2, Decl(constantExpressions1.ts, 25, 5)) +} + diff --git a/tests/baselines/reference/constantExpressions1.types b/tests/baselines/reference/constantExpressions1.types new file mode 100644 index 0000000000000..0bf409e31448f --- /dev/null +++ b/tests/baselines/reference/constantExpressions1.types @@ -0,0 +1,138 @@ +//// [tests/cases/compiler/constantExpressions1.ts] //// + +=== constantExpressions1.ts === +const C00 = "a"; +>C00 : "a" +> : ^^^ +>"a" : "a" +> : ^^^ + +const C01 = "b" as const; +>C01 : "b" +> : ^^^ +>"b" as const : "b" +> : ^^^ +>"b" : "b" +> : ^^^ + +const C02: "c" = "c"; +>C02 : "c" +> : ^^^ +>"c" : "c" +> : ^^^ + +declare const C03: "d"; +>C03 : "d" +> : ^^^ + +const enum E0 { +>E0 : E0 +> : ^^ + + A = C00, +>A : E0.A +> : ^^^^ +>C00 : "a" +> : ^^^ + + B = C01, +>B : E0.B +> : ^^^^ +>C01 : "b" +> : ^^^ + + C = C02, +>C : E0.C +> : ^^^^ +>C02 : "c" +> : ^^^ + + D = C03, +>D : E0.D +> : ^^^^ +>C03 : "d" +> : ^^^ +} + +const C10 = 1; +>C10 : 1 +> : ^ +>1 : 1 +> : ^ + +const C11 = 2 as const; +>C11 : 2 +> : ^ +>2 as const : 2 +> : ^ +>2 : 2 +> : ^ + +const C12: 3 = 3; +>C12 : 3 +> : ^ +>3 : 3 +> : ^ + +declare const C13: 4; +>C13 : 4 +> : ^ + +const enum E1 { +>E1 : E1 +> : ^^ + + A = C10, +>A : E1.A +> : ^^^^ +>C10 : 1 +> : ^ + + B = C11, +>B : E1.B +> : ^^^^ +>C11 : 2 +> : ^ + + C = C12, +>C : E1.C +> : ^^^^ +>C12 : 3 +> : ^ + + D = C13, +>D : E1.D +> : ^^^^ +>C13 : 4 +> : ^ +} + +const C1: string = "x"; +>C1 : string +> : ^^^^^^ +>"x" : "x" +> : ^^^ + +const C2: "x" | "y" = "x"; +>C2 : "x" | "y" +> : ^^^^^^^^^ +>"x" : "x" +> : ^^^ + +const enum EE { +>EE : EE +> : ^^ + + A = C1, // Error +>A : EE.A +> : ^^^^ +>C1 : string +> : ^^^^^^ + + B = C2, // Error +>B : EE.B +> : ^^^^ +>C2 : "x" +> : ^^^ +} + diff --git a/tests/cases/compiler/constantExpressions1.ts b/tests/cases/compiler/constantExpressions1.ts new file mode 100644 index 0000000000000..19c329d224f21 --- /dev/null +++ b/tests/cases/compiler/constantExpressions1.ts @@ -0,0 +1,34 @@ +// @strict: true +// @declaration: true + +const C00 = "a"; +const C01 = "b" as const; +const C02: "c" = "c"; +declare const C03: "d"; + +const enum E0 { + A = C00, + B = C01, + C = C02, + D = C03, +} + +const C10 = 1; +const C11 = 2 as const; +const C12: 3 = 3; +declare const C13: 4; + +const enum E1 { + A = C10, + B = C11, + C = C12, + D = C13, +} + +const C1: string = "x"; +const C2: "x" | "y" = "x"; + +const enum EE { + A = C1, // Error + B = C2, // Error +}