diff --git a/lib/coffeescript/nodes.js b/lib/coffeescript/nodes.js index 4a9e1f4a13..7790e0217b 100644 --- a/lib/coffeescript/nodes.js +++ b/lib/coffeescript/nodes.js @@ -2630,10 +2630,23 @@ // Add an expression to the class initializer - // NOTE Currently, only methods and static methods are valid in ES class initializers. - // When additional expressions become valid, this method should be updated to handle them. + // This is the key method for determining whether an expression in a class + // body should appear in the initializer or the executable body. If the given + // `node` is valid in a class body the method will return a (new, modified, + // or identical) node for inclusion in the class initializer, otherwise + // nothing will be returned and the node will appear in the executable body. + + // At time of writing, only methods (instance and static) are valid in ES + // class initializers. As new ES class features (such as class fields) reach + // Stage 4, this method will need to be updated to support them. We + // additionally allow `PassthroughLiteral`s (backticked expressions) in the + // initializer as an escape hatch for ES features that are not implemented + // (e.g. getters and setters defined via the `get` and `set` keywords as + // opposed to the `Object.defineProperty` method). addInitializerExpression(node) { - if (this.validInitializerMethod(node)) { + if (node.unwrapAll() instanceof PassthroughLiteral) { + return node; + } else if (this.validInitializerMethod(node)) { return this.addInitializerMethod(node); } else { return null; diff --git a/src/nodes.coffee b/src/nodes.coffee index ba03134295..452225e022 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -1752,10 +1752,23 @@ exports.Class = class Class extends Base # Add an expression to the class initializer # - # NOTE Currently, only methods and static methods are valid in ES class initializers. - # When additional expressions become valid, this method should be updated to handle them. + # This is the key method for determining whether an expression in a class + # body should appear in the initializer or the executable body. If the given + # `node` is valid in a class body the method will return a (new, modified, + # or identical) node for inclusion in the class initializer, otherwise + # nothing will be returned and the node will appear in the executable body. + # + # At time of writing, only methods (instance and static) are valid in ES + # class initializers. As new ES class features (such as class fields) reach + # Stage 4, this method will need to be updated to support them. We + # additionally allow `PassthroughLiteral`s (backticked expressions) in the + # initializer as an escape hatch for ES features that are not implemented + # (e.g. getters and setters defined via the `get` and `set` keywords as + # opposed to the `Object.defineProperty` method). addInitializerExpression: (node) -> - if @validInitializerMethod node + if node.unwrapAll() instanceof PassthroughLiteral + node + else if @validInitializerMethod node @addInitializerMethod node else null diff --git a/test/classes.coffee b/test/classes.coffee index c4366f215a..75474b9069 100644 --- a/test/classes.coffee +++ b/test/classes.coffee @@ -1833,3 +1833,18 @@ test "#4591: super.x.y, super['x'].y", -> eq 2, b.t eq 2, b.s eq 2, b.r + +test "#4464: backticked expressions in class body", -> + class A + `get x() { return 42; }` + + class B + `get x() { return 42; }` + constructor: -> + @y = 84 + + a = new A + eq 42, a.x + b = new B + eq 42, b.x + eq 84, b.y