diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9284465eefc28..f9c87e949615d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -31785,6 +31785,9 @@ namespace ts { return grammarErrorAtPos(node, node.end - 1, ";".length, Diagnostics._0_expected, "{"); } } + else if (isClassLike(node.parent) && isStringLiteral(node.name) && node.name.text === "constructor" && (!compilerOptions.target || compilerOptions.target < ScriptTarget.ES5)) { + return grammarErrorOnNode(node.name, Diagnostics.Quoted_constructors_have_previously_been_interpreted_as_methods_which_is_incorrect_In_TypeScript_3_6_they_will_be_correctly_parsed_as_constructors_In_the_meantime_consider_using_constructor_to_write_a_constructor_or_constructor_to_write_a_method); + } if (checkGrammarForGenerator(node)) { return true; } @@ -32103,6 +32106,9 @@ namespace ts { function checkGrammarProperty(node: PropertyDeclaration | PropertySignature) { if (isClassLike(node.parent)) { + if (isStringLiteral(node.name) && node.name.text === "constructor") { + return grammarErrorOnNode(node.name, Diagnostics.Classes_may_not_have_a_field_named_constructor); + } if (checkGrammarForInvalidDynamicName(node.name, Diagnostics.A_computed_property_name_in_a_class_property_declaration_must_refer_to_an_expression_whose_type_is_a_literal_type_or_a_unique_symbol_type)) { return true; } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 8342a4dd6c4d5..af3ecad79a669 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -4965,5 +4965,13 @@ "No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer." :{ "category": "Error", "code": 18004 + }, + "Quoted constructors have previously been interpreted as methods, which is incorrect. In TypeScript 3.6, they will be correctly parsed as constructors. In the meantime, consider using 'constructor()' to write a constructor, or '[\"constructor\"]()' to write a method.": { + "category": "Error", + "code": 18005 + }, + "Classes may not have a field named 'constructor'.": { + "category": "Error", + "code": 18006 } } diff --git a/tests/baselines/reference/convertKeywordsYes.errors.txt b/tests/baselines/reference/convertKeywordsYes.errors.txt index 7218e1fd12c66..e142e6de03d7f 100644 --- a/tests/baselines/reference/convertKeywordsYes.errors.txt +++ b/tests/baselines/reference/convertKeywordsYes.errors.txt @@ -1,3 +1,4 @@ +tests/cases/compiler/convertKeywordsYes.ts(175,12): error TS18006: Classes may not have a field named 'constructor'. tests/cases/compiler/convertKeywordsYes.ts(292,11): error TS1213: Identifier expected. 'implements' is a reserved word in strict mode. Class definitions are automatically in strict mode. tests/cases/compiler/convertKeywordsYes.ts(293,11): error TS1213: Identifier expected. 'interface' is a reserved word in strict mode. Class definitions are automatically in strict mode. tests/cases/compiler/convertKeywordsYes.ts(294,11): error TS1213: Identifier expected. 'let' is a reserved word in strict mode. Class definitions are automatically in strict mode. @@ -9,7 +10,7 @@ tests/cases/compiler/convertKeywordsYes.ts(301,11): error TS1213: Identifier exp tests/cases/compiler/convertKeywordsYes.ts(303,11): error TS1213: Identifier expected. 'yield' is a reserved word in strict mode. Class definitions are automatically in strict mode. -==== tests/cases/compiler/convertKeywordsYes.ts (9 errors) ==== +==== tests/cases/compiler/convertKeywordsYes.ts (10 errors) ==== // reserved ES5 future in strict mode var constructor = 0; @@ -185,6 +186,8 @@ tests/cases/compiler/convertKeywordsYes.ts(303,11): error TS1213: Identifier exp class bigClass { public "constructor" = 0; + ~~~~~~~~~~~~~ +!!! error TS18006: Classes may not have a field named 'constructor'. public any = 0; public boolean = 0; public implements = 0; diff --git a/tests/baselines/reference/propertyNamedConstructor.errors.txt b/tests/baselines/reference/propertyNamedConstructor.errors.txt new file mode 100644 index 0000000000000..e275131a50c36 --- /dev/null +++ b/tests/baselines/reference/propertyNamedConstructor.errors.txt @@ -0,0 +1,14 @@ +tests/cases/conformance/classes/propertyMemberDeclarations/propertyNamedConstructor.ts(2,3): error TS18006: Classes may not have a field named 'constructor'. + + +==== tests/cases/conformance/classes/propertyMemberDeclarations/propertyNamedConstructor.ts (1 errors) ==== + class X1 { + "constructor" = 3; // Error + ~~~~~~~~~~~~~ +!!! error TS18006: Classes may not have a field named 'constructor'. + } + + class X2 { + ["constructor"] = 3; + } + \ No newline at end of file diff --git a/tests/baselines/reference/propertyNamedConstructor.js b/tests/baselines/reference/propertyNamedConstructor.js new file mode 100644 index 0000000000000..8b7752ab6d558 --- /dev/null +++ b/tests/baselines/reference/propertyNamedConstructor.js @@ -0,0 +1,23 @@ +//// [propertyNamedConstructor.ts] +class X1 { + "constructor" = 3; // Error +} + +class X2 { + ["constructor"] = 3; +} + + +//// [propertyNamedConstructor.js] +var X1 = /** @class */ (function () { + function X1() { + this["constructor"] = 3; // Error + } + return X1; +}()); +var X2 = /** @class */ (function () { + function X2() { + this["constructor"] = 3; + } + return X2; +}()); diff --git a/tests/baselines/reference/propertyNamedConstructor.symbols b/tests/baselines/reference/propertyNamedConstructor.symbols new file mode 100644 index 0000000000000..b8a028d8444b8 --- /dev/null +++ b/tests/baselines/reference/propertyNamedConstructor.symbols @@ -0,0 +1,16 @@ +=== tests/cases/conformance/classes/propertyMemberDeclarations/propertyNamedConstructor.ts === +class X1 { +>X1 : Symbol(X1, Decl(propertyNamedConstructor.ts, 0, 0)) + + "constructor" = 3; // Error +>"constructor" : Symbol(X1["constructor"], Decl(propertyNamedConstructor.ts, 0, 10)) +} + +class X2 { +>X2 : Symbol(X2, Decl(propertyNamedConstructor.ts, 2, 1)) + + ["constructor"] = 3; +>["constructor"] : Symbol(X2["constructor"], Decl(propertyNamedConstructor.ts, 4, 10)) +>"constructor" : Symbol(X2["constructor"], Decl(propertyNamedConstructor.ts, 4, 10)) +} + diff --git a/tests/baselines/reference/propertyNamedConstructor.types b/tests/baselines/reference/propertyNamedConstructor.types new file mode 100644 index 0000000000000..6b90f31e917ad --- /dev/null +++ b/tests/baselines/reference/propertyNamedConstructor.types @@ -0,0 +1,18 @@ +=== tests/cases/conformance/classes/propertyMemberDeclarations/propertyNamedConstructor.ts === +class X1 { +>X1 : X1 + + "constructor" = 3; // Error +>"constructor" : number +>3 : 3 +} + +class X2 { +>X2 : X2 + + ["constructor"] = 3; +>["constructor"] : number +>"constructor" : "constructor" +>3 : 3 +} + diff --git a/tests/baselines/reference/quotedConstructors.errors.txt b/tests/baselines/reference/quotedConstructors.errors.txt new file mode 100644 index 0000000000000..9e70452ff4062 --- /dev/null +++ b/tests/baselines/reference/quotedConstructors.errors.txt @@ -0,0 +1,30 @@ +tests/cases/conformance/classes/constructorDeclarations/quotedConstructors.ts(2,5): error TS18005: Quoted constructors have previously been interpreted as methods, which is incorrect. In TypeScript 3.6, they will be correctly parsed as constructors. In the meantime, consider using 'constructor()' to write a constructor, or '["constructor"]()' to write a method. +tests/cases/conformance/classes/constructorDeclarations/quotedConstructors.ts(6,5): error TS18005: Quoted constructors have previously been interpreted as methods, which is incorrect. In TypeScript 3.6, they will be correctly parsed as constructors. In the meantime, consider using 'constructor()' to write a constructor, or '["constructor"]()' to write a method. +tests/cases/conformance/classes/constructorDeclarations/quotedConstructors.ts(14,5): error TS18005: Quoted constructors have previously been interpreted as methods, which is incorrect. In TypeScript 3.6, they will be correctly parsed as constructors. In the meantime, consider using 'constructor()' to write a constructor, or '["constructor"]()' to write a method. + + +==== tests/cases/conformance/classes/constructorDeclarations/quotedConstructors.ts (3 errors) ==== + class C { + "constructor"() {} // Error in 3.5 + ~~~~~~~~~~~~~ +!!! error TS18005: Quoted constructors have previously been interpreted as methods, which is incorrect. In TypeScript 3.6, they will be correctly parsed as constructors. In the meantime, consider using 'constructor()' to write a constructor, or '["constructor"]()' to write a method. + } + + class D { + 'constructor'() {} // Error in 3.5 + ~~~~~~~~~~~~~ +!!! error TS18005: Quoted constructors have previously been interpreted as methods, which is incorrect. In TypeScript 3.6, they will be correctly parsed as constructors. In the meantime, consider using 'constructor()' to write a constructor, or '["constructor"]()' to write a method. + } + + class E { + ['constructor']() {} + } + + new class { + "constructor"() {} // Error in 3.5 + ~~~~~~~~~~~~~ +!!! error TS18005: Quoted constructors have previously been interpreted as methods, which is incorrect. In TypeScript 3.6, they will be correctly parsed as constructors. In the meantime, consider using 'constructor()' to write a constructor, or '["constructor"]()' to write a method. + }; + + var o = { "constructor"() {} }; + \ No newline at end of file diff --git a/tests/baselines/reference/quotedConstructors.js b/tests/baselines/reference/quotedConstructors.js new file mode 100644 index 0000000000000..c9a662e6af526 --- /dev/null +++ b/tests/baselines/reference/quotedConstructors.js @@ -0,0 +1,46 @@ +//// [quotedConstructors.ts] +class C { + "constructor"() {} // Error in 3.5 +} + +class D { + 'constructor'() {} // Error in 3.5 +} + +class E { + ['constructor']() {} +} + +new class { + "constructor"() {} // Error in 3.5 +}; + +var o = { "constructor"() {} }; + + +//// [quotedConstructors.js] +var C = /** @class */ (function () { + function C() { + } + C.prototype["constructor"] = function () { }; // Error in 3.5 + return C; +}()); +var D = /** @class */ (function () { + function D() { + } + D.prototype['constructor'] = function () { }; // Error in 3.5 + return D; +}()); +var E = /** @class */ (function () { + function E() { + } + E.prototype['constructor'] = function () { }; + return E; +}()); +new /** @class */ (function () { + function class_1() { + } + class_1.prototype["constructor"] = function () { }; // Error in 3.5 + return class_1; +}()); +var o = { "constructor": function () { } }; diff --git a/tests/baselines/reference/quotedConstructors.symbols b/tests/baselines/reference/quotedConstructors.symbols new file mode 100644 index 0000000000000..be68e48484754 --- /dev/null +++ b/tests/baselines/reference/quotedConstructors.symbols @@ -0,0 +1,33 @@ +=== tests/cases/conformance/classes/constructorDeclarations/quotedConstructors.ts === +class C { +>C : Symbol(C, Decl(quotedConstructors.ts, 0, 0)) + + "constructor"() {} // Error in 3.5 +>"constructor" : Symbol(C["constructor"], Decl(quotedConstructors.ts, 0, 9)) +} + +class D { +>D : Symbol(D, Decl(quotedConstructors.ts, 2, 1)) + + 'constructor'() {} // Error in 3.5 +>'constructor' : Symbol(D['constructor'], Decl(quotedConstructors.ts, 4, 9)) +} + +class E { +>E : Symbol(E, Decl(quotedConstructors.ts, 6, 1)) + + ['constructor']() {} +>['constructor'] : Symbol(E['constructor'], Decl(quotedConstructors.ts, 8, 9)) +>'constructor' : Symbol(E['constructor'], Decl(quotedConstructors.ts, 8, 9)) +} + +new class { + "constructor"() {} // Error in 3.5 +>"constructor" : Symbol((Anonymous class)["constructor"], Decl(quotedConstructors.ts, 12, 11)) + +}; + +var o = { "constructor"() {} }; +>o : Symbol(o, Decl(quotedConstructors.ts, 16, 3)) +>"constructor" : Symbol("constructor", Decl(quotedConstructors.ts, 16, 9)) + diff --git a/tests/baselines/reference/quotedConstructors.types b/tests/baselines/reference/quotedConstructors.types new file mode 100644 index 0000000000000..907a2152a80ac --- /dev/null +++ b/tests/baselines/reference/quotedConstructors.types @@ -0,0 +1,37 @@ +=== tests/cases/conformance/classes/constructorDeclarations/quotedConstructors.ts === +class C { +>C : C + + "constructor"() {} // Error in 3.5 +>"constructor" : () => void +} + +class D { +>D : D + + 'constructor'() {} // Error in 3.5 +>'constructor' : () => void +} + +class E { +>E : E + + ['constructor']() {} +>['constructor'] : () => void +>'constructor' : "constructor" +} + +new class { +>new class { "constructor"() {} // Error in 3.5} : (Anonymous class) +>class { "constructor"() {} // Error in 3.5} : typeof (Anonymous class) + + "constructor"() {} // Error in 3.5 +>"constructor" : () => void + +}; + +var o = { "constructor"() {} }; +>o : { "constructor"(): void; } +>{ "constructor"() {} } : { "constructor"(): void; } +>"constructor" : () => void + diff --git a/tests/cases/conformance/classes/constructorDeclarations/quotedConstructors.ts b/tests/cases/conformance/classes/constructorDeclarations/quotedConstructors.ts new file mode 100644 index 0000000000000..3ab45c72810cd --- /dev/null +++ b/tests/cases/conformance/classes/constructorDeclarations/quotedConstructors.ts @@ -0,0 +1,17 @@ +class C { + "constructor"() {} // Error in 3.5 +} + +class D { + 'constructor'() {} // Error in 3.5 +} + +class E { + ['constructor']() {} +} + +new class { + "constructor"() {} // Error in 3.5 +}; + +var o = { "constructor"() {} }; diff --git a/tests/cases/conformance/classes/propertyMemberDeclarations/propertyNamedConstructor.ts b/tests/cases/conformance/classes/propertyMemberDeclarations/propertyNamedConstructor.ts new file mode 100644 index 0000000000000..49c53a9894f05 --- /dev/null +++ b/tests/cases/conformance/classes/propertyMemberDeclarations/propertyNamedConstructor.ts @@ -0,0 +1,7 @@ +class X1 { + "constructor" = 3; // Error +} + +class X2 { + ["constructor"] = 3; +}