Issue 1:
Bug

There is a difference between class Base and class Base extends Object that shouldn't be there.

Classes explicitly extending Object (and their subclasses) are always instances of Object and are not instances of the respective classes.

Function calls fail on classes explicitly extending Object (and their subclasses).

Code demonstrating the differences between Babel & Node

Output of the console.log()'s are added as comments to the lines.

class Base1 extends Object {
constructor(name) {
super();
this.name = name;
}
getName() {
return this.name;
}
}

class Base2 {
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}

let b = new Base1("name1");
// --BABEL-- // --NODE--
console.log(b instanceof Object); // true // true
console.log(b instanceof Base1); // false // true
console.log(b.constructor.name); // Object // Base1
console.log(b.getName()); // TypeError: b.getName is not a function // name1
console.log(b); // { name: 'name1' } // Base1 { name: 'name1' }

console.log();

b = new Base2("name2");
// --BABEL & NODE--
console.log(b instanceof Object); // true
console.log(b instanceof Base2); // true
console.log(b.constructor.name); // Base2
console.log(b.getName()); // name2
console.log(b); // Base2 { name: 'name2' }

Babel Configuration

.babelrc

{
"presets": ["es2015", "es2016", "es2017"]
}

package.json / cli command (file is in src folder and result in lib folder)

"devDependencies": {
"babel-cli": "^6.26.0",
"babel-polyfill": "^6.26.0",
"babel-preset-es2015": "^6.24.1",
"babel-preset-es2016": "^6.24.1",
"babel-preset-es2017": "^6.24.1"
},
"scripts": {
"build": "babel src --out-dir lib",
"watch": "babel src -w --out-dir lib"
}

Expected Behavior

There should be no difference between instances created with class Base and class Base extends Object.

Current Behavior

class Base instances behave correctly.

class Base extends Object instances fail to implement functions and the correct type (constructor name, instanceof).

Context

class Base extends Object is needed for static type checker Flow to accept obj['propName'] = 123.

Environment

software
version(s)

Babel
6.26.0

node
8.3.0

npm
5.3.0

Operating System
macOS 10.12 & Windows 10

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.