opened
brad4d
username_0
I believe the right way to do this is to transpile the call to `super(...)` like this.
```javascript
class FooError extends Error {
  /**
   * @param {string} msg
   * @param {string|!Error=} cause
   */
  constructor(msg, cause = undefined) {
    super(msg);
    /** @const */ this.cause = cause;
  }
```
becomes
```javascript
/**
 * @constructor @struct
 * @param {string} msg
 * @param {string|!Error=} cause
 * @extends {Error}
 */
var FooError = function(msg, cause) {
  var $jscomp$tmp$error = new Error(msg);
  /** @const */ this.message = $jscomp$tmp$error.message;
  /** @const */ this.stack = $jscomp$tmp$error.stack;
  /** @const */ this.cause = cause;
};
$jscomp.inherits(FooError, Error);
```

Unfortunately, there's some code I've seen in the wild (angular2) that does its own workaround and will be broken by this change.
e.g.
```javascript
class FooError extends Error {
  /**
   * @param {string} msg
   * @param {string|!Error=} cause
   */
  constructor(msg, cause = undefined) {
    // This code assumes that super() will return a new Error object, but the spec says it should return
    // the (possibly new) value of this.
    const nativeError = super(msg);
    /** @const @private */ nativeError_ = nativeError;
    /** @const */ this.cause = cause;
  }
  // getters and setters to forward accesses to nativeError.
}
```
Make extension of Error with ES6 classes work correctly.
issue
created
brad4d
username_0
Forgot something in my suggested transpilation.
The spec says that the value returned by `super()` must be the (possibly replaced) value of `this`.
So, if we have `const e = super(msg);`, that should transpile to
```javascript
var $jscomp$tmp$error = new Error(msg);
/** @const */ this.message = $jscomp$tmp$error.message;
/** @const */ this.stack = $jscomp$tmp$error.stack;
/** @const */ var e = this;
```
comment
created
mprobst
username_1
Ah, ES6 and Errors. I wonder if we can find a code pattern that:

* allows user code to work correctly before being compiled
* without creating a secondary `Error` object indirection
* and gets correctly compiled by JSCompiler

By the way, it seems like this compiler transformation changes the semantics of the original JavaScript code that the user wrote. After all, according to spec, extending `Error` is supposed to work one way, and during compilation we change it here to work another way. I'd be a bit worried about unintended side effects of that, and about the semantic mismatch between compiled and non-compiled code. WDYT?
comment
created
ChadKillingsworth
username_2
I've never seen this and I've looked. I've seen plenty of posts saying that it can't be done (which doesn't mean they are correct). Also comes up when extending `Date` and `Promise`.
comment
created
brad4d
username_0
The ES6 spec effectively says this:
- The prototype of the class being created gets propagated up the chain of super classes to the highest one.
- An object using that prototype gets implicitly created and passed as `this` to the most-super class constructor. From there (barring `super()` returning some other value), all constructors will act on this object, in order.

What the transpiled code I suggested is doing is this.
- An object using the prototype of the class being created is implicitly created and passed to the child class constructor.
- Because we cannot successfully pass this instance to the Error constructor for initialization, we instead create a new Error & copy the results of its initialization (the two fields) over to the object that has the correct prototype chain.

This isn't ideal but has close-enough to the same result.
I'm only proposing this for the `*Error` classes, because
- Inheriting from them is such a clearly desirable thing to do.
- There are only a small number of well-known fields to be copied.

One thing I failed to make clear...
The example of how this is currently written in Angular2, by saving the value returned by calling `super()`, won't work uncompiled according to the spec. The spec says that `super()` should always return `this`, which means the `this.nativeError_` it stores is a circular reference to itself. This will make all of the get and set methods that redirect to it into infinite loops.
comment
created
brad4d
username_0
See also this advice for creating custom error types.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error#Custom_Error_Types
comment
created
brad4d
username_0
Fix for this submitted internally. It will be in the next push to github.
comment
closed
blickly
username_3
issue