babel / babel/babel

Edge case; functions in object literals cannot be constructors

Open
#5,237 2 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
44k
Forks
6k
Avg merge
5d 15h
Merged PRs (30d)
23

Description

Problem: ES 6 code works when transpiled to ES 5 with Babel, but not when running the same ES 6 straight in the browser without transpiling.

### Context
It is a somewhat crazy edge case, which can easily be disregarded as too stupid to bother with fixing, so I'll have to explain a bit of the background.

Backbone collections can be polymorphic; depending on which parameters are given for a model. A typical use case in Backbone is that you get a JSON list of items from a backend, and Backbone turns the response into a collection for you.
Here is an example of a monomorphic collection in Backbone, it only accepts Helicopters:

```javascript
// ES 5 code, works
var Helicopter = Backbone.Model.extend({});
var HelicopterCollection = Backbone.Model.extend({
model: Helicopter // Backbone instantiates the models for us
});
```

Here is an example of a polymorphic collection, which can contain both Helicopters and Planes:

```javascript
// ES 5 code, works
var Helicopter = Backbone.Model.extend({});
var Plane = Backbone.Model.extend({});
var VehicleCollection = Backbone.Collection.extend({
// now it's not a simple constructor any longer, it's a factory
model: function (attrs, options) {
return attrs.type === 'helicopter' ? new Helicopter(attrs) : new Plane(attrs);
}
});
```

This is [what Backbone does](https://github.com/jashkenas/backbone/blob/master/backbone.js#L1141):

```javascript
// inside the Backbone library, when a new model is going to be created
var model = new this.model(attrs, options);
```

### Input Code (to Babel)

So, assume you want to upgrade your application to ES 6, and use Babel to support old browsers. Here, the example is updated to ES 6:

```javascript
// Works when you use Babel, but not when running without Babel
const VehicleCollection = Backbone.Collection.extend({
model (attrs, options) { // <-- NB! note the use of object literal/shorthand notation here
return attrs.type === 'helicopter' ? new Helicopter(attrs) : new Plane(attrs);
}
});
```

### Babel Configuration
The code can be copy-pasted into the Babel online REPL (https://babeljs.io/repl/). Babel version 6.22.1, preset ES 2015.

### Babel output

```javascript
'use strict';

var VehicleCollection = Backbone.Collection.extend({
model: function model(attrs, options) {
return attrs.type === 'helicopter' ? new Helicopter(attrs) : new Plane(attrs);
}
});
```

### An easier example (without Backbone) that reduces the problem to its core

```javascript
class Plane {}

const withShorthand = {
model () {
return new Plane();
}
};

const full = {
model: function () {
return new Plane();
},
};

console.log('Result with full notation/function keyword', new full.model());
console.log('Result with shorthand notation', new withShorthand.model());
```

### Expected Behavior (of the easier example)

When running the ES 6 code as a Node.js script without babel, this is the console output:

```
Result with full notation/function keyword Plane {}

console.log('Result with shorthand notation', new withShorthand.model());
^
TypeError: withShorthand.model is not a constructor
at Object. ...
...
```

### Current Behavior
This is the console output when running the Babel-transpiled code:

```
Result with full notation/function keyword Plane {}
Result with shorthand notation Plane {}
```

As you can see, the object literal functions are allowed to be used as constructors.

### Possible solution

When transpiling object literals, one could add a check in the functions to make sure that they are not called as constructors - and if they are, throw a TypeError like V8 does when running ES 6 straight.
I'm not sure how expensive the is-a-constructor check is though - that could be a concern.

Contributor guide

Open the contributing guide

Research direction

Reproduce the minimal object-literal example in the Babel online REPL and compare its output with native ES6 behavior. The issue names no repository file or test; done means transpiled shorthand methods preserve the observed non-constructor behavior without breaking ordinary method calls.

Written by the indexing model from the issue text.

Assessment

Tech stack
babel, javascript
Domain
compilers
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.