Edge case; functions in object literals cannot be constructors
- 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
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