Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extending ES6 Map causes error - Constructor Map requires 'new' #10853

Closed
flyon opened this issue Sep 11, 2016 · 10 comments
Closed

Extending ES6 Map causes error - Constructor Map requires 'new' #10853

flyon opened this issue Sep 11, 2016 · 10 comments
Labels
External Relates to another program, environment, or user action which we cannot control.

Comments

@flyon
Copy link

flyon commented Sep 11, 2016

I know native types should be extendable since this PR #3516 , but I can't seem to get extending ES6 Map to work.

The following code

class ResourceMap extends Map {}
var x = new ResourceMap();
x.set('foo',true);

compiles correctly to:

var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var ResourceMap = (function (_super) {
    __extends(ResourceMap, _super);
    function ResourceMap() {
        _super.apply(this, arguments);
    }
    return ResourceMap;
}(Map));

//[...]
var x = new ResourceMap();
x.set('foo',true);

But when I run it in chrome it gives an error Uncaught TypeError: Constructor Map requires 'new'

When I run the original typescript code directly in the chrome console (which is possible because it's also valid ES6 and only uses functions supported by chrome) then it works correctly. So it should be possible?

Is there a way to extend ES6 Map from Typescript?

@flyon
Copy link
Author

flyon commented Sep 11, 2016

It seems I found a workaround for now with a static method to create new instances (could not get it to work with constructors). Not sure if there is any other implications of doing it this way.

export default class ExampleMap extends Map<string,string> {
    foo() {
        return 'bar';
    }
    static create (array?:any[]):ExampleMap {
        var inst = new Map(array);
        inst['__proto__'] = ExampleMap.prototype;
        return inst as ExampleMap;
    }
}
var x = ExampleMap.create();
x.set("x","y"); // works
x.foo(); //<-- returns bar

@Arnavion
Copy link
Contributor

Arnavion commented Sep 11, 2016

Chrome (i.e. V8) does not provide an ES5 way to extend Map, so there's nothing TS can really do here. You can search for that error and find similar issues with babel, traceur, livescript, etc.

Your workaround to use __proto__ is technically illegal ES5 as well since __proto__ was only standardized in ES6, so TS can't use it either.

@RyanCavanaugh RyanCavanaugh added the External Relates to another program, environment, or user action which we cannot control. label Sep 12, 2016
@mhegazy mhegazy closed this as completed Sep 20, 2016
@vtulin
Copy link

vtulin commented May 22, 2017

Can the following be used as workaround? It actually works with warnings.

Map.prototype.apply = function () { };

class ResourceMap extends new Map { }

let m = new ResourceMap();

console.log(m);

@vtulin
Copy link

vtulin commented May 22, 2017

No, sorry that creates another ton of errors.

@pmunin
Copy link

pmunin commented Oct 9, 2017

Please reopen the issue. Chrome V8 (v61) allows to extend map with no problem now, but typescript still does not work:

//This code works fine in Chrome
class MyMap extends Map{
constructor(){
super();
}

test(){return "Hallo world";}

}

var myMap = new MyMap();
console.log(myMap instanceof Map); //true
console.log(myMap.test()); //"Hallo world"

@Stradivario
Copy link

Just change your compilerOptions inside tsconfig.json
"compilerOptions": {
"target": "es6",
}

new Map operator is a ES6 feature so you need to target your exported js to ES 6 Syntax

Regards,
Kristiyan Tachev

@Xample
Copy link

Xample commented Jan 24, 2018

@vtulin I haven't checked but you can probably return the Map from the constructor. Something like this:

class myMap {
constructor(){
return new Map();
}
}

The only problem is that typescript does not know this object actually inherits from the Map. I found a similar trick here: https://stackoverflow.com/a/40714458/532695

@Stradivario
Copy link

@Xample it is not correct to return something inside constructor since constructor is a VOID type function.

@Xample
Copy link

Xample commented Feb 20, 2018

@Stradivario no it's not a void, it's the type of the class, the constructor implicitly returns thisfor you. For instance, if you do write an interface typing the constructor taking a string as an input you will have:

interface TypedConstructor {
    new (parameter:string): TypedConstructor;
}

It's the way it is in ES6, more reading

@DavidBM
Copy link

DavidBM commented Mar 31, 2018

What is the update about this issue? We should be able to extend Set and Map now :/

@microsoft microsoft locked and limited conversation to collaborators Jul 25, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
External Relates to another program, environment, or user action which we cannot control.
Projects
None yet
Development

No branches or pull requests

9 participants