TypeScript: Mixed es5/es2015 code with mixin classes causes runtime errors
TypeScript Version: 2.4.1 (but likely anything since 2.2)
When utilising code targeted at ES5 mixed with code targeted at ES6 causes a runtime error when using mixin classes. The real world use case was using a library that was targeted at ES5 for distribution compatibility reasons while downstream code is being targeted at ES2017 because of running in a known limited environment.
Code
Tagged.ts
// @target: es5
export interface Constructor<T> {
new(...args: any[]): T;
prototype: T;
}
export default function Tagged<T extends Constructor<{}>>(Base: T) {
return class extends Base {
_tag = '';
}
}
TaggedExample.ts
// @target: es2015
import Tagged from './Tagged';
class Example {
log() {
console.log('Hello World!');
}
}
const TaggedExample = Tagged(Example);
const taggedExample = new TaggedExample(); // Uncaught TypeError: Class constructor Example cannot be invoked without 'new'
Expected behavior:
No run-time errors.
Actual behavior:
A runtime error of Uncaught TypeError: Class constructor Example cannot be invoked without 'new'.
About this issue
- Original URL
- State: open
- Created 7 years ago
- Reactions: 6
- Comments: 15 (9 by maintainers)
This is very problematic because it means my library (which rely on this) published in es5 cannot be consumed by JavaScript directly at all.
This issue still exists, but as more and more projects move to targeting ES2015 and higher I’m not sure it is a high enough priority to be fixed as the chances of running afoul of this are smaller and smaller. I expected the largest cause of this comes from extending builtins like
MapandSet, but extending builtins seems to be something TC39 members are actively discouraging these days (with the possible exception ofError).@DanielRosenwasser do we want to close this, or take a fix? I put together a fix for this, but it requires adding the
__constructhelper I mentioned above, which would change the emit for everyone using subclassing and--target ES5. It also would require a change totslib, and that would require anyone affected by the change to upgrade to the latesttslib.Alternatively, we could fix this behind a flag for the rare cases where this happens.
An example how to reproduce is here: https://www.typescriptlang.org/play?target=1&jsx=0&ts=4.9.4&filetype=ts#code/KYNwhgNgFABgUAAgQYwmAzuhAhDwEDeiSKA9gHboAuATgK7JWk1QCUhxJZlpEwAdBFIBzKAHJc6YAC5kFavUbMxrANycSVABYBLdIJFt1XAL4aho9kS5I5PPgdES80iyuMkzSLwgDuO8gATUl9+SXwAXhw8dRg1ODhUDCwAYV0IQIRgAA8qYCCsKH8gkIQMMvIAT1YwvA5beVoGJhYrDXQ6AAdgVo8G+wELcTSdDNlGxRb3YjNiIbabbnReQcMxEbG3eO84WbtqFHTMqPJgXwQNwKM4AHobrOzuxnRb+-DxyialGleLo9cRL9Lh8FM1mHAgA
And this is how it fails: https://stackblitz.com/edit/js-svplky?file=index.js
I flag would be awesome.
Looks like this works with Babel, because it creates a function around the super class (
_createSuperhelper):and
new B()works.TS uses
A.apply(..)directly, which is invalid with native classes:Because this fails:
Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.
🎤 tap tap is this thing on? 😁
/cc @mhegazy