Issue 1:
Issue originally made by @slikts

Bug information

Babel version: 6.7.7

Input code

class Foo extends Array {}

Description

Context here: https://esdiscuss.org/topic/adding-a-non-class-syntax-method-for-subclassing-native-objects

The Reflect.construct() method allows calling the internal [[Construct]] method, which results in the constructed object inheriting the full behavior of exotic objects; Babel should use Reflect.construct() if it exists in the runtime to preserve the full semantics of the class extends syntax, which allows inheriting the exotic object behavior.

Issue 2:
Current Babel transform, when it comes to call the parent

function _possibleConstructorReturn(self, call) {
if (!self) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return call && (typeof call === "object" || typeof call === "function") ? call : self;
}

It's a way too poor implementation.

If we take the current basic ES6 compat syntax:

class List extends Array {}

We'll realize babel does a bad job.

var l = new List();
l instanceof List; // false

The reason is simple: Babel replace the returns and exit, without caring about userland expectations.

This is how above basic extend should desugar:

function List() {
return Object.setPrototypeOf(
Array.apply(this, arguments),
List.prototype
);
}

It's a very ad-hoc case for the initial example that currently fails, but it's good enough to understand that inheriting the prototype is the least of the problem.

Indeed, we have 3 ways to do that within a transpiled code:

// losing the initial prototype
// for polyfilled ES5+ or lower
List.prototype = Object.create(
Array.prototype,
{constructor: {
configurable: true,
writable: true,
value: List.prototype
}}
);

// using a cleaner way
// for ES5+ compliant targets
Object.setPrototypeOf(
List.prototype,
Array.prototype
);

// using a cleaner way
// that works well with
// partially polyfilled .setPrototypeOf
List.prototype = Object.setPrototypeOf(
List.prototype,
Array.prototype
);

Solved the inheritance bit, considering Babel also set the prototype of each constructor,

we need to address cases where a super call might "upgrade" the current context, like it is for HTMLELement or exotic native objects.

// native ES6 static example
class List extends Array {
constructor(a, b, c) {
super(a, b);
this.push(c);
}
}

Above case should desugar to something like the follwoing:

function List(a, b, c) {
// the super bit
var self = Object.setPrototypeOf(
Array.call(this, a, b),
List.prototype
);
// the rest with swapped context
self.push(c);
// at the end
return self;
}

which is also ad-hoc example code for the previous example.

Considering a transpiler will get the arguments part easily right, this is how previous case could be generically transpiled for arguments used in both constructors:

// to make it as generic as possible
function Child() {

// the super call bit desugar to ...
var
instance = Object.getPrototypeOf(Child)
.apply(this, arguments),
type = instance ? typeof instance : '',
// if Parent overwrote its default return
self = (type === 'object' || type === 'function') ?
// upgrade the instance to reflect Child constructor
Object.setPrototypeOf(instance, Child.prototype) :
// otherwise use the current context as is
this
;

// at this point, the rest of the constructor
// should use `self` instead of `this`
self.push(c);

// and return `self` reference at the end
return self;
}

The last problem is that modern syntax would use Reflect to create any sort of object, instead of old, ES3 friendly, .call or .apply way.

Following a past, present, and even future proof approach:

var
reConstruct = typeof Reflect === 'object' ?
Reflect.construct :
function (Parent, args, Child) {
return Parent.apply(this, args);
}
;

function Child() {

// the super call bit
var
instance = reConstruct.call(
this,
Object.getPrototypeOf(Child),
arguments,
Child
),
type = instance ? typeof instance : '',
self = (type === 'object' || type === 'function') ?
Object.setPrototypeOf(instance, Child.prototype) :
this
;

// the rest of the constructor body
self.push(c);

// at the end or instead of empty returns
return self;
}

Above solution would work with userland, exotic, or DOM constructors, and in both native and transpiled engines.