Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Added test for new useDefineForClassFields default and error message.
  • Loading branch information
dragomirtitian committed Apr 5, 2021
commit b3021b276fb7b6c6f86b31bdd09c6ad9c6c9d603
4 changes: 2 additions & 2 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26986,8 +26986,8 @@ namespace ts {
const lexicalValueDecl = lexicallyScopedSymbol.valueDeclaration;
const lexicalClass = lexicalValueDecl && getContainingClass(lexicalValueDecl);
const parentStaticFieldInitializer = findAncestor(node, (n) => {
if(n == lexicalClass) return "quit";
if(isPropertyDeclaration(n.parent) && n.parent.initializer == n && n.parent.parent === lexicalClass) {
if(n === lexicalClass) return "quit";
if(isPropertyDeclaration(n.parent) && n.parent.initializer === n && n.parent.parent === lexicalClass) {
return true;
}
return false;
Expand Down
10 changes: 5 additions & 5 deletions src/compiler/transformers/classFields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ namespace ts {
const resolver = context.getEmitResolver();
const compilerOptions = context.getCompilerOptions();
const languageVersion = getEmitScriptTarget(compilerOptions);
const useDefineForClassFields = getUseDefineForClassFields(compilerOptions);

const shouldTransformPrivateElements = languageVersion < ScriptTarget.ESNext;

Expand Down Expand Up @@ -138,7 +139,7 @@ namespace ts {
function transformSourceFile(node: SourceFile) {
const options = context.getCompilerOptions();
if (node.isDeclarationFile
|| options.useDefineForClassFields && options.target === ScriptTarget.ESNext) {
|| useDefineForClassFields && options.target === ScriptTarget.ESNext) {
return node;
}
const visited = visitEachChild(node, visitor, context);
Expand Down Expand Up @@ -340,7 +341,7 @@ namespace ts {
// Create a temporary variable to store a computed property name (if necessary).
// If it's not inlineable, then we emit an expression after the class which assigns
// the property name to the temporary variable.
const expr = getPropertyNameExpressionIfNeeded(node.name, !!node.initializer || !!context.getCompilerOptions().useDefineForClassFields);
const expr = getPropertyNameExpressionIfNeeded(node.name, !!node.initializer || useDefineForClassFields);
if (expr && !isSimpleInlineableExpression(expr)) {
getPendingExpressions().push(expr);
}
Expand Down Expand Up @@ -829,7 +830,7 @@ namespace ts {
if (hasStaticModifier(member) || hasSyntacticModifier(getOriginalNode(member), ModifierFlags.Abstract)) {
return false;
}
if (context.getCompilerOptions().useDefineForClassFields) {
if (useDefineForClassFields) {
// If we are using define semantics and targeting ESNext or higher,
// then we don't need to transform any class properties.
return languageVersion < ScriptTarget.ESNext;
Expand Down Expand Up @@ -865,7 +866,6 @@ namespace ts {
}

function transformConstructorBody(node: ClassDeclaration | ClassExpression, constructor: ConstructorDeclaration | undefined, isDerivedClass: boolean) {
const useDefineForClassFields = context.getCompilerOptions().useDefineForClassFields;
let properties = getProperties(node, /*requireInitializer*/ false, /*isStatic*/ false);
if (!useDefineForClassFields) {
properties = filter(properties, property => !!property.initializer || isPrivateIdentifier(property.name));
Expand Down Expand Up @@ -1000,7 +1000,7 @@ namespace ts {
*/
function transformProperty(property: PropertyDeclaration, receiver: LeftHandSideExpression) {
// We generate a name here in order to reuse the value cached by the relocated computed name expression (which uses the same generated name)
const emitAssignment = !context.getCompilerOptions().useDefineForClassFields;
const emitAssignment = !useDefineForClassFields;
const propertyName = isComputedPropertyName(property.name) && !isSimpleInlineableExpression(property.name.expression)
? factory.updateComputedPropertyName(property.name, factory.getGeneratedNameForNode(property.name))
: property.name;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//// [privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts]
class Test {
#prop = 0
static dd = new Test().#prop; // Err
static ["X_ z_ zz"] = class Inner {
#foo = 10
m() {
new Test().#prop // Err
}
static C = class InnerInner {
m() {
new Test().#prop // Err
new Inner().#foo; // Err
}
}

static M(){
return class {
m() {
new Test().#prop // Err
new Inner().#foo; // OK
}
}
}
}
}

//// [privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.js]
"use strict";
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, privateMap) {
if (!privateMap.has(receiver)) {
throw new TypeError("attempted to get private field on non-instance");
}
return privateMap.get(receiver);
};
var _prop, _foo, _a;
class Test {
constructor() {
_prop.set(this, 0);
}
}
_prop = new WeakMap();
Test.dd = __classPrivateFieldGet(new Test(), _prop); // Err
Test["X_ z_ zz"] = (_a = class Inner {
constructor() {
_foo.set(this, 10);
}
m() {
__classPrivateFieldGet(new Test(), _prop); // Err
}
static M() {
return class {
m() {
__classPrivateFieldGet(new Test(), _prop); // Err
__classPrivateFieldGet(new Inner(), _foo); // OK
}
};
}
},
_foo = new WeakMap(),
_a.C = class InnerInner {
m() {
__classPrivateFieldGet(new Test(), _prop); // Err
__classPrivateFieldGet(new _a(), _foo); // Err
}
},
_a);
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
=== tests/cases/conformance/classes/members/privateNames/privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts ===
class Test {
>Test : Symbol(Test, Decl(privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts, 0, 0))

#prop = 0
>#prop : Symbol(Test.#prop, Decl(privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts, 0, 12))

static dd = new Test().#prop; // Err
>dd : Symbol(Test.dd, Decl(privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts, 1, 13))
>new Test().#prop : Symbol(Test.#prop, Decl(privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts, 0, 12))
>Test : Symbol(Test, Decl(privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts, 0, 0))

static ["X_ z_ zz"] = class Inner {
>["X_ z_ zz"] : Symbol(Test["X_ z_ zz"], Decl(privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts, 2, 33))
>"X_ z_ zz" : Symbol(Test["X_ z_ zz"], Decl(privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts, 2, 33))
>Inner : Symbol(Inner, Decl(privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts, 3, 25))

#foo = 10
>#foo : Symbol(Inner.#foo, Decl(privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts, 3, 39))

m() {
>m : Symbol(Inner.m, Decl(privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts, 4, 18))

new Test().#prop // Err
>new Test().#prop : Symbol(Test.#prop, Decl(privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts, 0, 12))
>Test : Symbol(Test, Decl(privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts, 0, 0))
}
static C = class InnerInner {
>C : Symbol(Inner.C, Decl(privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts, 7, 9))
>InnerInner : Symbol(InnerInner, Decl(privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts, 8, 18))

m() {
>m : Symbol(InnerInner.m, Decl(privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts, 8, 37))

new Test().#prop // Err
>new Test().#prop : Symbol(Test.#prop, Decl(privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts, 0, 12))
>Test : Symbol(Test, Decl(privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts, 0, 0))

new Inner().#foo; // Err
>new Inner().#foo : Symbol(Inner.#foo, Decl(privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts, 3, 39))
>Inner : Symbol(Inner, Decl(privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts, 3, 25))
}
}

static M(){
>M : Symbol(Inner.M, Decl(privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts, 13, 9))

return class {
m() {
>m : Symbol((Anonymous class).m, Decl(privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts, 16, 26))

new Test().#prop // Err
>new Test().#prop : Symbol(Test.#prop, Decl(privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts, 0, 12))
>Test : Symbol(Test, Decl(privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts, 0, 0))

new Inner().#foo; // OK
>new Inner().#foo : Symbol(Inner.#foo, Decl(privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts, 3, 39))
>Inner : Symbol(Inner, Decl(privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts, 3, 25))
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
=== tests/cases/conformance/classes/members/privateNames/privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts ===
class Test {
>Test : Test

#prop = 0
>#prop : number
>0 : 0

static dd = new Test().#prop; // Err
>dd : number
>new Test().#prop : number
>new Test() : Test
>Test : typeof Test

static ["X_ z_ zz"] = class Inner {
>["X_ z_ zz"] : typeof Inner
>"X_ z_ zz" : "X_ z_ zz"
>class Inner { #foo = 10 m() { new Test().#prop // Err } static C = class InnerInner { m() { new Test().#prop // Err new Inner().#foo; // Err } } static M(){ return class { m() { new Test().#prop // Err new Inner().#foo; // OK } } } } : typeof Inner
>Inner : typeof Inner

#foo = 10
>#foo : number
>10 : 10

m() {
>m : () => void

new Test().#prop // Err
>new Test().#prop : number
>new Test() : Test
>Test : typeof Test
}
static C = class InnerInner {
>C : typeof InnerInner
>class InnerInner { m() { new Test().#prop // Err new Inner().#foo; // Err } } : typeof InnerInner
>InnerInner : typeof InnerInner

m() {
>m : () => void

new Test().#prop // Err
>new Test().#prop : number
>new Test() : Test
>Test : typeof Test

new Inner().#foo; // Err
>new Inner().#foo : number
>new Inner() : Inner
>Inner : typeof Inner
}
}

static M(){
>M : () => typeof (Anonymous class)

return class {
>class { m() { new Test().#prop // Err new Inner().#foo; // OK } } : typeof (Anonymous class)

m() {
>m : () => void

new Test().#prop // Err
>new Test().#prop : number
>new Test() : Test
>Test : typeof Test

new Inner().#foo; // OK
>new Inner().#foo : number
>new Inner() : Inner
>Inner : typeof Inner
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
tests/cases/conformance/classes/members/privateNames/privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts(3,17): error TS2801: Property '#prop' has a private name but is used in a static initializer in its declaring class. This is only supported for an ESNext target if 'useDefineForClassFields' is set to 'true'
tests/cases/conformance/classes/members/privateNames/privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts(7,13): error TS2801: Property '#prop' has a private name but is used in a static initializer in its declaring class. This is only supported for an ESNext target if 'useDefineForClassFields' is set to 'true'
tests/cases/conformance/classes/members/privateNames/privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts(11,17): error TS2801: Property '#prop' has a private name but is used in a static initializer in its declaring class. This is only supported for an ESNext target if 'useDefineForClassFields' is set to 'true'
tests/cases/conformance/classes/members/privateNames/privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts(12,17): error TS2801: Property '#foo' has a private name but is used in a static initializer in its declaring class. This is only supported for an ESNext target if 'useDefineForClassFields' is set to 'true'
tests/cases/conformance/classes/members/privateNames/privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts(19,21): error TS2801: Property '#prop' has a private name but is used in a static initializer in its declaring class. This is only supported for an ESNext target if 'useDefineForClassFields' is set to 'true'


==== tests/cases/conformance/classes/members/privateNames/privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts (5 errors) ====
class Test {
#prop = 0
static dd = new Test().#prop; // Err
~~~~~~~~~~~~~~~~
!!! error TS2801: Property '#prop' has a private name but is used in a static initializer in its declaring class. This is only supported for an ESNext target if 'useDefineForClassFields' is set to 'true'
!!! related TS2802 tests/cases/conformance/classes/members/privateNames/privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts:3:12: Initializer for property 'dd'
static ["X_ z_ zz"] = class Inner {
#foo = 10
m() {
new Test().#prop // Err
~~~~~~~~~~~~~~~~
!!! error TS2801: Property '#prop' has a private name but is used in a static initializer in its declaring class. This is only supported for an ESNext target if 'useDefineForClassFields' is set to 'true'
!!! related TS2802 tests/cases/conformance/classes/members/privateNames/privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts:4:12: Initializer for property 'X_ z_ zz'
}
static C = class InnerInner {
m() {
new Test().#prop // Err
~~~~~~~~~~~~~~~~
!!! error TS2801: Property '#prop' has a private name but is used in a static initializer in its declaring class. This is only supported for an ESNext target if 'useDefineForClassFields' is set to 'true'
!!! related TS2802 tests/cases/conformance/classes/members/privateNames/privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts:4:12: Initializer for property 'X_ z_ zz'
new Inner().#foo; // Err
~~~~~~~~~~~~~~~~
!!! error TS2801: Property '#foo' has a private name but is used in a static initializer in its declaring class. This is only supported for an ESNext target if 'useDefineForClassFields' is set to 'true'
!!! related TS2802 tests/cases/conformance/classes/members/privateNames/privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts:9:16: Initializer for property 'C'
}
}

static M(){
return class {
m() {
new Test().#prop // Err
~~~~~~~~~~~~~~~~
!!! error TS2801: Property '#prop' has a private name but is used in a static initializer in its declaring class. This is only supported for an ESNext target if 'useDefineForClassFields' is set to 'true'
!!! related TS2802 tests/cases/conformance/classes/members/privateNames/privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts:4:12: Initializer for property 'X_ z_ zz'
new Inner().#foo; // OK
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//// [privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.ts]
class Test {
#prop = 0
static dd = new Test().#prop; // Err
static ["X_ z_ zz"] = class Inner {
#foo = 10
m() {
new Test().#prop // Err
}
static C = class InnerInner {
m() {
new Test().#prop // Err
new Inner().#foo; // Err
}
}

static M(){
return class {
m() {
new Test().#prop // Err
new Inner().#foo; // OK
}
}
}
}
}

//// [privateNameErrorsOnNotUseDefineForClassFieldsInEsNext.js]
"use strict";
var _a;
class Test {
constructor() {
this.#prop = 0;
}
#prop;
}
Test.dd = new Test().#prop; // Err
Test["X_ z_ zz"] = (_a = class Inner {
constructor() {
this.#foo = 10;
}
#foo;
m() {
new Test().#prop; // Err
}
static M() {
return class {
m() {
new Test().#prop; // Err
new Inner().#foo; // OK
}
};
}
},
_a.C = class InnerInner {
m() {
new Test().#prop; // Err
new _a().#foo; // Err
}
},
_a);
Loading