diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ce56db4b962f4..97bfa81630db2 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -21973,6 +21973,13 @@ namespace ts { const arg = (node).operand; return op === SyntaxKind.MinusToken && (arg.kind === SyntaxKind.NumericLiteral || arg.kind === SyntaxKind.BigIntLiteral) || op === SyntaxKind.PlusToken && arg.kind === SyntaxKind.NumericLiteral; + case SyntaxKind.PropertyAccessExpression: + case SyntaxKind.ElementAccessExpression: + const expr = (node).expression; + if (isIdentifier(expr)) { + const symbol = getSymbolAtLocation(expr); + return !!(symbol && (symbol.flags & SymbolFlags.Enum) && getEnumKind(symbol) === EnumKind.Literal); + } } return false; } @@ -21981,7 +21988,7 @@ namespace ts { let exprType = checkExpression(expression, checkMode); if (isConstTypeReference(type)) { if (!isValidConstAssertionArgument(expression)) { - error(expression, Diagnostics.A_const_assertion_can_only_be_applied_to_a_string_number_boolean_array_or_object_literal); + error(expression, Diagnostics.A_const_assertions_can_only_be_applied_to_references_to_enum_members_or_string_number_boolean_array_or_object_literals); } return getRegularTypeOfLiteralType(exprType); } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index a78e89f42beb3..04d8213dacbc6 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1031,7 +1031,7 @@ "category": "Error", "code": 1354 }, - "A 'const' assertion can only be applied to a string, number, boolean, array, or object literal.": { + "A 'const' assertions can only be applied to references to enum members, or string, number, boolean, array, or object literals.": { "category": "Error", "code": 1355 }, diff --git a/tests/baselines/reference/constAssertions.errors.txt b/tests/baselines/reference/constAssertions.errors.txt index 24fce1599012a..8ecce1377e344 100644 --- a/tests/baselines/reference/constAssertions.errors.txt +++ b/tests/baselines/reference/constAssertions.errors.txt @@ -1,7 +1,7 @@ tests/cases/conformance/expressions/typeAssertions/constAssertions.ts(44,32): error TS2540: Cannot assign to 'x' because it is a read-only property. -tests/cases/conformance/expressions/typeAssertions/constAssertions.ts(61,10): error TS1355: A 'const' assertion can only be applied to a string, number, boolean, array, or object literal. -tests/cases/conformance/expressions/typeAssertions/constAssertions.ts(62,10): error TS1355: A 'const' assertion can only be applied to a string, number, boolean, array, or object literal. -tests/cases/conformance/expressions/typeAssertions/constAssertions.ts(63,10): error TS1355: A 'const' assertion can only be applied to a string, number, boolean, array, or object literal. +tests/cases/conformance/expressions/typeAssertions/constAssertions.ts(61,10): error TS1355: A 'const' assertions can only be applied to references to enum members, or string, number, boolean, array, or object literals. +tests/cases/conformance/expressions/typeAssertions/constAssertions.ts(62,10): error TS1355: A 'const' assertions can only be applied to references to enum members, or string, number, boolean, array, or object literals. +tests/cases/conformance/expressions/typeAssertions/constAssertions.ts(63,10): error TS1355: A 'const' assertions can only be applied to references to enum members, or string, number, boolean, array, or object literals. ==== tests/cases/conformance/expressions/typeAssertions/constAssertions.ts (4 errors) ==== @@ -69,11 +69,11 @@ tests/cases/conformance/expressions/typeAssertions/constAssertions.ts(63,10): er let e1 = v1 as const; // Error ~~ -!!! error TS1355: A 'const' assertion can only be applied to a string, number, boolean, array, or object literal. +!!! error TS1355: A 'const' assertions can only be applied to references to enum members, or string, number, boolean, array, or object literals. let e2 = (true ? 1 : 0) as const; // Error ~~~~~~~~~~~~~~ -!!! error TS1355: A 'const' assertion can only be applied to a string, number, boolean, array, or object literal. +!!! error TS1355: A 'const' assertions can only be applied to references to enum members, or string, number, boolean, array, or object literals. let e3 = id(1) as const; // Error ~~~~~ -!!! error TS1355: A 'const' assertion can only be applied to a string, number, boolean, array, or object literal. +!!! error TS1355: A 'const' assertions can only be applied to references to enum members, or string, number, boolean, array, or object literals. \ No newline at end of file diff --git a/tests/baselines/reference/constantEnumAssert.errors.txt b/tests/baselines/reference/constantEnumAssert.errors.txt new file mode 100644 index 0000000000000..b384d3bd308e0 --- /dev/null +++ b/tests/baselines/reference/constantEnumAssert.errors.txt @@ -0,0 +1,59 @@ +tests/cases/compiler/constantEnumAssert.ts(45,20): error TS1355: A 'const' assertions can only be applied to references to enum members, or string, number, boolean, array, or object literals. +tests/cases/compiler/constantEnumAssert.ts(49,20): error TS1355: A 'const' assertions can only be applied to references to enum members, or string, number, boolean, array, or object literals. + + +==== tests/cases/compiler/constantEnumAssert.ts (2 errors) ==== + enum E1 { + a, + b + } + + enum E2 { + a = 'a', + b = 'b' + } + + enum E3 { + a = 1, + b = a << 1, + c = a << 2, + } + + const enum E4 { + a, + b + } + + const E5 = { + a: 'a', + b: 'b' + } + + const foo1 = { a: E1.a } + + const foo2 = { a: E2.a } + + const foo3 = { a: E1.a } as const + + const foo4 = { a: E2.a } as const + + const foo5 = { a: E3.a } as const + + const foo6 = { a: E4.a } as const + + const foo7 = { a: E5.a } as const + + const foo8 = { a: E1.a as const } + + const foo9 = { a: E2.a as const } + + const foo10 = { a: E3.a as const } + ~~~~ +!!! error TS1355: A 'const' assertions can only be applied to references to enum members, or string, number, boolean, array, or object literals. + + const foo11 = { a: E4.a as const } + + const foo12 = { a: E5.a as const } + ~~~~ +!!! error TS1355: A 'const' assertions can only be applied to references to enum members, or string, number, boolean, array, or object literals. + \ No newline at end of file diff --git a/tests/baselines/reference/constantEnumAssert.js b/tests/baselines/reference/constantEnumAssert.js new file mode 100644 index 0000000000000..85f3db9d93a77 --- /dev/null +++ b/tests/baselines/reference/constantEnumAssert.js @@ -0,0 +1,85 @@ +//// [constantEnumAssert.ts] +enum E1 { + a, + b +} + +enum E2 { + a = 'a', + b = 'b' +} + +enum E3 { + a = 1, + b = a << 1, + c = a << 2, +} + +const enum E4 { + a, + b +} + +const E5 = { + a: 'a', + b: 'b' +} + +const foo1 = { a: E1.a } + +const foo2 = { a: E2.a } + +const foo3 = { a: E1.a } as const + +const foo4 = { a: E2.a } as const + +const foo5 = { a: E3.a } as const + +const foo6 = { a: E4.a } as const + +const foo7 = { a: E5.a } as const + +const foo8 = { a: E1.a as const } + +const foo9 = { a: E2.a as const } + +const foo10 = { a: E3.a as const } + +const foo11 = { a: E4.a as const } + +const foo12 = { a: E5.a as const } + + +//// [constantEnumAssert.js] +var E1; +(function (E1) { + E1[E1["a"] = 0] = "a"; + E1[E1["b"] = 1] = "b"; +})(E1 || (E1 = {})); +var E2; +(function (E2) { + E2["a"] = "a"; + E2["b"] = "b"; +})(E2 || (E2 = {})); +var E3; +(function (E3) { + E3[E3["a"] = 1] = "a"; + E3[E3["b"] = 2] = "b"; + E3[E3["c"] = 4] = "c"; +})(E3 || (E3 = {})); +var E5 = { + a: 'a', + b: 'b' +}; +var foo1 = { a: E1.a }; +var foo2 = { a: E2.a }; +var foo3 = { a: E1.a }; +var foo4 = { a: E2.a }; +var foo5 = { a: E3.a }; +var foo6 = { a: 0 /* a */ }; +var foo7 = { a: E5.a }; +var foo8 = { a: E1.a }; +var foo9 = { a: E2.a }; +var foo10 = { a: E3.a }; +var foo11 = { a: 0 /* a */ }; +var foo12 = { a: E5.a }; diff --git a/tests/baselines/reference/constantEnumAssert.symbols b/tests/baselines/reference/constantEnumAssert.symbols new file mode 100644 index 0000000000000..3249dbe61ccaa --- /dev/null +++ b/tests/baselines/reference/constantEnumAssert.symbols @@ -0,0 +1,140 @@ +=== tests/cases/compiler/constantEnumAssert.ts === +enum E1 { +>E1 : Symbol(E1, Decl(constantEnumAssert.ts, 0, 0)) + + a, +>a : Symbol(E1.a, Decl(constantEnumAssert.ts, 0, 9)) + + b +>b : Symbol(E1.b, Decl(constantEnumAssert.ts, 1, 6)) +} + +enum E2 { +>E2 : Symbol(E2, Decl(constantEnumAssert.ts, 3, 1)) + + a = 'a', +>a : Symbol(E2.a, Decl(constantEnumAssert.ts, 5, 9)) + + b = 'b' +>b : Symbol(E2.b, Decl(constantEnumAssert.ts, 6, 12)) +} + +enum E3 { +>E3 : Symbol(E3, Decl(constantEnumAssert.ts, 8, 1)) + + a = 1, +>a : Symbol(E3.a, Decl(constantEnumAssert.ts, 10, 9)) + + b = a << 1, +>b : Symbol(E3.b, Decl(constantEnumAssert.ts, 11, 10)) +>a : Symbol(E3.a, Decl(constantEnumAssert.ts, 10, 9)) + + c = a << 2, +>c : Symbol(E3.c, Decl(constantEnumAssert.ts, 12, 15)) +>a : Symbol(E3.a, Decl(constantEnumAssert.ts, 10, 9)) +} + +const enum E4 { +>E4 : Symbol(E4, Decl(constantEnumAssert.ts, 14, 1)) + + a, +>a : Symbol(E4.a, Decl(constantEnumAssert.ts, 16, 15)) + + b +>b : Symbol(E4.b, Decl(constantEnumAssert.ts, 17, 6)) +} + +const E5 = { +>E5 : Symbol(E5, Decl(constantEnumAssert.ts, 21, 5)) + + a: 'a', +>a : Symbol(a, Decl(constantEnumAssert.ts, 21, 12)) + + b: 'b' +>b : Symbol(b, Decl(constantEnumAssert.ts, 22, 11)) +} + +const foo1 = { a: E1.a } +>foo1 : Symbol(foo1, Decl(constantEnumAssert.ts, 26, 5)) +>a : Symbol(a, Decl(constantEnumAssert.ts, 26, 14)) +>E1.a : Symbol(E1.a, Decl(constantEnumAssert.ts, 0, 9)) +>E1 : Symbol(E1, Decl(constantEnumAssert.ts, 0, 0)) +>a : Symbol(E1.a, Decl(constantEnumAssert.ts, 0, 9)) + +const foo2 = { a: E2.a } +>foo2 : Symbol(foo2, Decl(constantEnumAssert.ts, 28, 5)) +>a : Symbol(a, Decl(constantEnumAssert.ts, 28, 14)) +>E2.a : Symbol(E2.a, Decl(constantEnumAssert.ts, 5, 9)) +>E2 : Symbol(E2, Decl(constantEnumAssert.ts, 3, 1)) +>a : Symbol(E2.a, Decl(constantEnumAssert.ts, 5, 9)) + +const foo3 = { a: E1.a } as const +>foo3 : Symbol(foo3, Decl(constantEnumAssert.ts, 30, 5)) +>a : Symbol(a, Decl(constantEnumAssert.ts, 30, 14)) +>E1.a : Symbol(E1.a, Decl(constantEnumAssert.ts, 0, 9)) +>E1 : Symbol(E1, Decl(constantEnumAssert.ts, 0, 0)) +>a : Symbol(E1.a, Decl(constantEnumAssert.ts, 0, 9)) + +const foo4 = { a: E2.a } as const +>foo4 : Symbol(foo4, Decl(constantEnumAssert.ts, 32, 5)) +>a : Symbol(a, Decl(constantEnumAssert.ts, 32, 14)) +>E2.a : Symbol(E2.a, Decl(constantEnumAssert.ts, 5, 9)) +>E2 : Symbol(E2, Decl(constantEnumAssert.ts, 3, 1)) +>a : Symbol(E2.a, Decl(constantEnumAssert.ts, 5, 9)) + +const foo5 = { a: E3.a } as const +>foo5 : Symbol(foo5, Decl(constantEnumAssert.ts, 34, 5)) +>a : Symbol(a, Decl(constantEnumAssert.ts, 34, 14)) +>E3.a : Symbol(E3.a, Decl(constantEnumAssert.ts, 10, 9)) +>E3 : Symbol(E3, Decl(constantEnumAssert.ts, 8, 1)) +>a : Symbol(E3.a, Decl(constantEnumAssert.ts, 10, 9)) + +const foo6 = { a: E4.a } as const +>foo6 : Symbol(foo6, Decl(constantEnumAssert.ts, 36, 5)) +>a : Symbol(a, Decl(constantEnumAssert.ts, 36, 14)) +>E4.a : Symbol(E4.a, Decl(constantEnumAssert.ts, 16, 15)) +>E4 : Symbol(E4, Decl(constantEnumAssert.ts, 14, 1)) +>a : Symbol(E4.a, Decl(constantEnumAssert.ts, 16, 15)) + +const foo7 = { a: E5.a } as const +>foo7 : Symbol(foo7, Decl(constantEnumAssert.ts, 38, 5)) +>a : Symbol(a, Decl(constantEnumAssert.ts, 38, 14)) +>E5.a : Symbol(a, Decl(constantEnumAssert.ts, 21, 12)) +>E5 : Symbol(E5, Decl(constantEnumAssert.ts, 21, 5)) +>a : Symbol(a, Decl(constantEnumAssert.ts, 21, 12)) + +const foo8 = { a: E1.a as const } +>foo8 : Symbol(foo8, Decl(constantEnumAssert.ts, 40, 5)) +>a : Symbol(a, Decl(constantEnumAssert.ts, 40, 14)) +>E1.a : Symbol(E1.a, Decl(constantEnumAssert.ts, 0, 9)) +>E1 : Symbol(E1, Decl(constantEnumAssert.ts, 0, 0)) +>a : Symbol(E1.a, Decl(constantEnumAssert.ts, 0, 9)) + +const foo9 = { a: E2.a as const } +>foo9 : Symbol(foo9, Decl(constantEnumAssert.ts, 42, 5)) +>a : Symbol(a, Decl(constantEnumAssert.ts, 42, 14)) +>E2.a : Symbol(E2.a, Decl(constantEnumAssert.ts, 5, 9)) +>E2 : Symbol(E2, Decl(constantEnumAssert.ts, 3, 1)) +>a : Symbol(E2.a, Decl(constantEnumAssert.ts, 5, 9)) + +const foo10 = { a: E3.a as const } +>foo10 : Symbol(foo10, Decl(constantEnumAssert.ts, 44, 5)) +>a : Symbol(a, Decl(constantEnumAssert.ts, 44, 15)) +>E3.a : Symbol(E3.a, Decl(constantEnumAssert.ts, 10, 9)) +>E3 : Symbol(E3, Decl(constantEnumAssert.ts, 8, 1)) +>a : Symbol(E3.a, Decl(constantEnumAssert.ts, 10, 9)) + +const foo11 = { a: E4.a as const } +>foo11 : Symbol(foo11, Decl(constantEnumAssert.ts, 46, 5)) +>a : Symbol(a, Decl(constantEnumAssert.ts, 46, 15)) +>E4.a : Symbol(E4.a, Decl(constantEnumAssert.ts, 16, 15)) +>E4 : Symbol(E4, Decl(constantEnumAssert.ts, 14, 1)) +>a : Symbol(E4.a, Decl(constantEnumAssert.ts, 16, 15)) + +const foo12 = { a: E5.a as const } +>foo12 : Symbol(foo12, Decl(constantEnumAssert.ts, 48, 5)) +>a : Symbol(a, Decl(constantEnumAssert.ts, 48, 15)) +>E5.a : Symbol(a, Decl(constantEnumAssert.ts, 21, 12)) +>E5 : Symbol(E5, Decl(constantEnumAssert.ts, 21, 5)) +>a : Symbol(a, Decl(constantEnumAssert.ts, 21, 12)) + diff --git a/tests/baselines/reference/constantEnumAssert.types b/tests/baselines/reference/constantEnumAssert.types new file mode 100644 index 0000000000000..36d4f7f3abcd4 --- /dev/null +++ b/tests/baselines/reference/constantEnumAssert.types @@ -0,0 +1,172 @@ +=== tests/cases/compiler/constantEnumAssert.ts === +enum E1 { +>E1 : E1 + + a, +>a : E1.a + + b +>b : E1.b +} + +enum E2 { +>E2 : E2 + + a = 'a', +>a : E2.a +>'a' : "a" + + b = 'b' +>b : E2.b +>'b' : "b" +} + +enum E3 { +>E3 : E3 + + a = 1, +>a : E3 +>1 : 1 + + b = a << 1, +>b : E3 +>a << 1 : number +>a : E3 +>1 : 1 + + c = a << 2, +>c : E3 +>a << 2 : number +>a : E3 +>2 : 2 +} + +const enum E4 { +>E4 : E4 + + a, +>a : E4.a + + b +>b : E4.b +} + +const E5 = { +>E5 : { a: string; b: string; } +>{ a: 'a', b: 'b'} : { a: string; b: string; } + + a: 'a', +>a : string +>'a' : "a" + + b: 'b' +>b : string +>'b' : "b" +} + +const foo1 = { a: E1.a } +>foo1 : { a: E1; } +>{ a: E1.a } : { a: E1; } +>a : E1 +>E1.a : E1.a +>E1 : typeof E1 +>a : E1.a + +const foo2 = { a: E2.a } +>foo2 : { a: E2; } +>{ a: E2.a } : { a: E2; } +>a : E2 +>E2.a : E2.a +>E2 : typeof E2 +>a : E2.a + +const foo3 = { a: E1.a } as const +>foo3 : { readonly a: E1.a; } +>{ a: E1.a } as const : { readonly a: E1.a; } +>{ a: E1.a } : { readonly a: E1.a; } +>a : E1.a +>E1.a : E1.a +>E1 : typeof E1 +>a : E1.a + +const foo4 = { a: E2.a } as const +>foo4 : { readonly a: E2.a; } +>{ a: E2.a } as const : { readonly a: E2.a; } +>{ a: E2.a } : { readonly a: E2.a; } +>a : E2.a +>E2.a : E2.a +>E2 : typeof E2 +>a : E2.a + +const foo5 = { a: E3.a } as const +>foo5 : { readonly a: E3; } +>{ a: E3.a } as const : { readonly a: E3; } +>{ a: E3.a } : { readonly a: E3; } +>a : E3 +>E3.a : E3 +>E3 : typeof E3 +>a : E3 + +const foo6 = { a: E4.a } as const +>foo6 : { readonly a: E4.a; } +>{ a: E4.a } as const : { readonly a: E4.a; } +>{ a: E4.a } : { readonly a: E4.a; } +>a : E4.a +>E4.a : E4.a +>E4 : typeof E4 +>a : E4.a + +const foo7 = { a: E5.a } as const +>foo7 : { readonly a: string; } +>{ a: E5.a } as const : { readonly a: string; } +>{ a: E5.a } : { readonly a: string; } +>a : string +>E5.a : string +>E5 : { a: string; b: string; } +>a : string + +const foo8 = { a: E1.a as const } +>foo8 : { a: E1.a; } +>{ a: E1.a as const } : { a: E1.a; } +>a : E1.a +>E1.a as const : E1.a +>E1.a : E1.a +>E1 : typeof E1 +>a : E1.a + +const foo9 = { a: E2.a as const } +>foo9 : { a: E2.a; } +>{ a: E2.a as const } : { a: E2.a; } +>a : E2.a +>E2.a as const : E2.a +>E2.a : E2.a +>E2 : typeof E2 +>a : E2.a + +const foo10 = { a: E3.a as const } +>foo10 : { a: E3; } +>{ a: E3.a as const } : { a: E3; } +>a : E3 +>E3.a as const : E3 +>E3.a : E3 +>E3 : typeof E3 +>a : E3 + +const foo11 = { a: E4.a as const } +>foo11 : { a: E4.a; } +>{ a: E4.a as const } : { a: E4.a; } +>a : E4.a +>E4.a as const : E4.a +>E4.a : E4.a +>E4 : typeof E4 +>a : E4.a + +const foo12 = { a: E5.a as const } +>foo12 : { a: string; } +>{ a: E5.a as const } : { a: string; } +>a : string +>E5.a as const : string +>E5.a : string +>E5 : { a: string; b: string; } +>a : string + diff --git a/tests/cases/compiler/constantEnumAssert.ts b/tests/cases/compiler/constantEnumAssert.ts new file mode 100644 index 0000000000000..d57845a7edfdd --- /dev/null +++ b/tests/cases/compiler/constantEnumAssert.ts @@ -0,0 +1,49 @@ +enum E1 { + a, + b +} + +enum E2 { + a = 'a', + b = 'b' +} + +enum E3 { + a = 1, + b = a << 1, + c = a << 2, +} + +const enum E4 { + a, + b +} + +const E5 = { + a: 'a', + b: 'b' +} + +const foo1 = { a: E1.a } + +const foo2 = { a: E2.a } + +const foo3 = { a: E1.a } as const + +const foo4 = { a: E2.a } as const + +const foo5 = { a: E3.a } as const + +const foo6 = { a: E4.a } as const + +const foo7 = { a: E5.a } as const + +const foo8 = { a: E1.a as const } + +const foo9 = { a: E2.a as const } + +const foo10 = { a: E3.a as const } + +const foo11 = { a: E4.a as const } + +const foo12 = { a: E5.a as const }