Repository: TypeScript
Issue: Proposal: Add new __construct helper for better ES5/ES6 class interop
Labels: Suggestion,Awaiting More Feedback

I propose we add a new helper to assist with class instance construction runtime semantics when extending ES6 built-in classes while compiling with `--target ES5`.

## Background
Our current emit for classes for `--target ES5` assumes that the superclass follows the same runtime semantics as the classes we emit. Generally this means that the constructor can be called as a function via `call()` or `apply()`. However, a number of ES6 built-in classes are specified to throw when not used as a constructor (i.e. `Promise`, `Map`, etc.), and other ES6 built-in classes return a value when called, ignoring the `this` value provided to `call()` or `apply()` (i.e. `Error`, `Array`, etc.).

Previously we [provided guidance](https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work) for possible workarounds for this to support the latter scenario, but we do not currently have a solution for the former scenario.

## Proposal

The following code listing describes a new `__construct` helper that we would need to emit for any file that contains an explicit (or implicit, for property declarations) `super()` call:

```ts
class MyPromise extends Promise {
  constructor(executor) {
    super(executor);
  }
}

// becomes...
var __extends = ...;
var __construct = (this && this.__construct) || (typeof Reflect !== "undefined" && Reflect.construct
    ? function (s, t, a, n) { return t !== null ? Reflect.construct(t, a, n) : s; }
    : function (s, t, a) { return t !== null && t.apply(s, a) || s; });

var MyPromise = (function (_super) {
  __extends(MyPromise, _super);
  function MyPromise(executor) {
    var _this = this;
    var _newTarget = this.constructor;
    _this = __construct(this, _super, [executor], _newTarget);
    return _this;
  }
  return MyPromise;
})(Promise);
```

### Benefits

* Allows down-level class emit to extend ES6 built-ins if running in an ES6 host by feature detecting `Reflect.construct`.
* Falls back to the [existing behavior](http://www.typescriptlang.org/play/index.html#src=var%20a%20%3D%20null%3B%0D%0Aclass%20C%20extends%20a%20%7B%0D%0A%20%20%20%20x%20%3D%201%3B%0D%0A%7D) when running in an ES5 host.
* Handles `extends null` and `extends x` when `x` is `null` in the same way as [existing behavior](http://www.typescriptlang.org/play/index.html#src=var%20a%20%3D%20null%3B%0D%0Aclass%20C%20extends%20a%20%7B%0D%0A%20%20%20%20x%20%3D%201%3B%0D%0A%7D).
* Handles custom return values from `super` in the same way as [existing behavior](http://www.typescriptlang.org/play/index.html#src=var%20a%20%3D%20null%3B%0D%0Aclass%20C%20extends%20a%20%7B%0D%0A%20%20%20%20x%20%3D%201%3B%0D%0A%7D).

### Drawbacks

* Larger helper footprint
* Subclassing a built-in in an ES5 host has different runtime semantics than subclassing a built-in in an ES6 host:
  * In ES5, subclassing `Array` or `Error` will not have the correct prototype chain. The only solution is to explicitly set the prototype chain using the non-standard `__proto__` property as per [established guidance](https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work).