diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2217f2f430b82..db0b3a6db9141 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1822,9 +1822,10 @@ namespace ts { // Perform extra checks only if error reporting was requested if (nameNotFoundMessage) { - if (propertyWithInvalidInitializer) { + if (propertyWithInvalidInitializer && !(compilerOptions.target === ScriptTarget.ESNext && compilerOptions.useDefineForClassFields)) { // We have a match, but the reference occurred within a property initializer and the identifier also binds - // to a local variable in the constructor where the code will be emitted. + // to a local variable in the constructor where the code will be emitted. Note that this is actually allowed + // with ESNext+useDefineForClassFields because the scope semantics are different. const propertyName = (propertyWithInvalidInitializer).name; error(errorLocation, Diagnostics.Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor, declarationNameToString(propertyName), diagnosticName(nameArg!)); diff --git a/tests/baselines/reference/constructorParameterShadowsOuterScopes2.errors.txt b/tests/baselines/reference/constructorParameterShadowsOuterScopes2.errors.txt new file mode 100644 index 0000000000000..7b2f3c6121a4b --- /dev/null +++ b/tests/baselines/reference/constructorParameterShadowsOuterScopes2.errors.txt @@ -0,0 +1,38 @@ +tests/cases/conformance/classes/propertyMemberDeclarations/constructorParameterShadowsOuterScopes2.ts(28,9): error TS2304: Cannot find name 'z'. + + +==== tests/cases/conformance/classes/propertyMemberDeclarations/constructorParameterShadowsOuterScopes2.ts (1 errors) ==== + // With useDefineForClassFields: true and ESNext target, initializer + // expressions for property declarations are evaluated in the scope of + // the class body and are permitted to reference parameters or local + // variables of the constructor. This is different from classic + // Typescript behaviour, with useDefineForClassFields: false. There, + // initialisers of property declarations are evaluated in the scope of + // the constructor body. + + // Note that when class fields are accepted in the ECMAScript + // standard, the target will become that year's ES20xx + + var x = 1; + class C { + b = x; // ok + constructor(x: string) { + } + } + + var y = 1; + class D { + b = y; // ok + constructor(x: string) { + var y = ""; + } + } + + class E { + b = z; // not ok + ~ +!!! error TS2304: Cannot find name 'z'. + constructor(z: string) { + } + } + \ No newline at end of file diff --git a/tests/baselines/reference/constructorParameterShadowsOuterScopes2.js b/tests/baselines/reference/constructorParameterShadowsOuterScopes2.js new file mode 100644 index 0000000000000..a266bc5b0fb19 --- /dev/null +++ b/tests/baselines/reference/constructorParameterShadowsOuterScopes2.js @@ -0,0 +1,62 @@ +//// [constructorParameterShadowsOuterScopes2.ts] +// With useDefineForClassFields: true and ESNext target, initializer +// expressions for property declarations are evaluated in the scope of +// the class body and are permitted to reference parameters or local +// variables of the constructor. This is different from classic +// Typescript behaviour, with useDefineForClassFields: false. There, +// initialisers of property declarations are evaluated in the scope of +// the constructor body. + +// Note that when class fields are accepted in the ECMAScript +// standard, the target will become that year's ES20xx + +var x = 1; +class C { + b = x; // ok + constructor(x: string) { + } +} + +var y = 1; +class D { + b = y; // ok + constructor(x: string) { + var y = ""; + } +} + +class E { + b = z; // not ok + constructor(z: string) { + } +} + + +//// [constructorParameterShadowsOuterScopes2.js] +// With useDefineForClassFields: true and ESNext target, initializer +// expressions for property declarations are evaluated in the scope of +// the class body and are permitted to reference parameters or local +// variables of the constructor. This is different from classic +// Typescript behaviour, with useDefineForClassFields: false. There, +// initialisers of property declarations are evaluated in the scope of +// the constructor body. +// Note that when class fields are accepted in the ECMAScript +// standard, the target will become that year's ES20xx +var x = 1; +class C { + b = x; // ok + constructor(x) { + } +} +var y = 1; +class D { + b = y; // ok + constructor(x) { + var y = ""; + } +} +class E { + b = z; // not ok + constructor(z) { + } +} diff --git a/tests/baselines/reference/constructorParameterShadowsOuterScopes2.symbols b/tests/baselines/reference/constructorParameterShadowsOuterScopes2.symbols new file mode 100644 index 0000000000000..01bb0bb7613fa --- /dev/null +++ b/tests/baselines/reference/constructorParameterShadowsOuterScopes2.symbols @@ -0,0 +1,56 @@ +=== tests/cases/conformance/classes/propertyMemberDeclarations/constructorParameterShadowsOuterScopes2.ts === +// With useDefineForClassFields: true and ESNext target, initializer +// expressions for property declarations are evaluated in the scope of +// the class body and are permitted to reference parameters or local +// variables of the constructor. This is different from classic +// Typescript behaviour, with useDefineForClassFields: false. There, +// initialisers of property declarations are evaluated in the scope of +// the constructor body. + +// Note that when class fields are accepted in the ECMAScript +// standard, the target will become that year's ES20xx + +var x = 1; +>x : Symbol(x, Decl(constructorParameterShadowsOuterScopes2.ts, 11, 3)) + +class C { +>C : Symbol(C, Decl(constructorParameterShadowsOuterScopes2.ts, 11, 10)) + + b = x; // ok +>b : Symbol(C.b, Decl(constructorParameterShadowsOuterScopes2.ts, 12, 9)) +>x : Symbol(x, Decl(constructorParameterShadowsOuterScopes2.ts, 11, 3)) + + constructor(x: string) { +>x : Symbol(x, Decl(constructorParameterShadowsOuterScopes2.ts, 14, 16)) + } +} + +var y = 1; +>y : Symbol(y, Decl(constructorParameterShadowsOuterScopes2.ts, 18, 3)) + +class D { +>D : Symbol(D, Decl(constructorParameterShadowsOuterScopes2.ts, 18, 10)) + + b = y; // ok +>b : Symbol(D.b, Decl(constructorParameterShadowsOuterScopes2.ts, 19, 9)) +>y : Symbol(y, Decl(constructorParameterShadowsOuterScopes2.ts, 18, 3)) + + constructor(x: string) { +>x : Symbol(x, Decl(constructorParameterShadowsOuterScopes2.ts, 21, 16)) + + var y = ""; +>y : Symbol(y, Decl(constructorParameterShadowsOuterScopes2.ts, 22, 11)) + } +} + +class E { +>E : Symbol(E, Decl(constructorParameterShadowsOuterScopes2.ts, 24, 1)) + + b = z; // not ok +>b : Symbol(E.b, Decl(constructorParameterShadowsOuterScopes2.ts, 26, 9)) + + constructor(z: string) { +>z : Symbol(z, Decl(constructorParameterShadowsOuterScopes2.ts, 28, 16)) + } +} + diff --git a/tests/baselines/reference/constructorParameterShadowsOuterScopes2.types b/tests/baselines/reference/constructorParameterShadowsOuterScopes2.types new file mode 100644 index 0000000000000..c9c24a45e5e6f --- /dev/null +++ b/tests/baselines/reference/constructorParameterShadowsOuterScopes2.types @@ -0,0 +1,60 @@ +=== tests/cases/conformance/classes/propertyMemberDeclarations/constructorParameterShadowsOuterScopes2.ts === +// With useDefineForClassFields: true and ESNext target, initializer +// expressions for property declarations are evaluated in the scope of +// the class body and are permitted to reference parameters or local +// variables of the constructor. This is different from classic +// Typescript behaviour, with useDefineForClassFields: false. There, +// initialisers of property declarations are evaluated in the scope of +// the constructor body. + +// Note that when class fields are accepted in the ECMAScript +// standard, the target will become that year's ES20xx + +var x = 1; +>x : number +>1 : 1 + +class C { +>C : C + + b = x; // ok +>b : number +>x : number + + constructor(x: string) { +>x : string + } +} + +var y = 1; +>y : number +>1 : 1 + +class D { +>D : D + + b = y; // ok +>b : number +>y : number + + constructor(x: string) { +>x : string + + var y = ""; +>y : string +>"" : "" + } +} + +class E { +>E : E + + b = z; // not ok +>b : any +>z : any + + constructor(z: string) { +>z : string + } +} + diff --git a/tests/cases/conformance/classes/propertyMemberDeclarations/constructorParameterShadowsOuterScopes2.ts b/tests/cases/conformance/classes/propertyMemberDeclarations/constructorParameterShadowsOuterScopes2.ts new file mode 100644 index 0000000000000..d6c5be3bd7957 --- /dev/null +++ b/tests/cases/conformance/classes/propertyMemberDeclarations/constructorParameterShadowsOuterScopes2.ts @@ -0,0 +1,35 @@ +// @target: esnext +// @useDefineForClassFields: true + + +// With useDefineForClassFields: true and ESNext target, initializer +// expressions for property declarations are evaluated in the scope of +// the class body and are permitted to reference parameters or local +// variables of the constructor. This is different from classic +// Typescript behaviour, with useDefineForClassFields: false. There, +// initialisers of property declarations are evaluated in the scope of +// the constructor body. + +// Note that when class fields are accepted in the ECMAScript +// standard, the target will become that year's ES20xx + +var x = 1; +class C { + b = x; // ok + constructor(x: string) { + } +} + +var y = 1; +class D { + b = y; // ok + constructor(x: string) { + var y = ""; + } +} + +class E { + b = z; // not ok + constructor(z: string) { + } +}