Skip to content

Object.constructor isn't implemented specifically enough #4586

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

Open
weswigham opened this issue Sep 1, 2015 · 4 comments
Open

Object.constructor isn't implemented specifically enough #4586

weswigham opened this issue Sep 1, 2015 · 4 comments
Labels
Bug A bug in TypeScript Help Wanted You can do this
Milestone

Comments

@weswigham
Copy link
Member

Consider:

class Foo extends ([].constructor) {
}

[].constructor should be Array, which we aught to be able to extend. However, the checker complains on [].constructor that "Type 'Function' is not a constructor function type". Considering .constructor is quite explicitly the constructor function for the object, I believe this is a lie perpetuated by our lib.d.ts files. I think we need to go add appropriate self-constructor-referencing .constructor fields to each builtin.

Additionally consider the following:

class Bar {
}

class Foo extends ((new Bar()).constructor) {
}

The same thing but with a custom class rather than a builtin. We should know the correct type of .constructor here (the Bar class), but we don't - we just look at the members on Object.

In the first example, I believe it could be fixed with more precise lib files, but in the second case, I believe this is an instance of a field whose type should be known by the compiler, much like .prototype on the class itself is known.

@kitsonk
Copy link
Contributor

kitsonk commented Sep 1, 2015

Dupe of #4356?

@weswigham
Copy link
Member Author

Partially? The last bit where .constructor should be compiler inferred is a dupe of that, for sure.

But in the immediate future, we should fix our lib.d.ts files so that for builtins it is actually typed to the constructor function and not Function, as in the [].constructor example above.

@mhegazy
Copy link
Contributor

mhegazy commented Sep 2, 2015

Feel free to send a PR for this.

@mhegazy mhegazy added Bug A bug in TypeScript Help Wanted You can do this labels Sep 2, 2015
@mhegazy mhegazy added this to the Community milestone Sep 18, 2015
@RyanCavanaugh RyanCavanaugh modified the milestones: Community, Backlog Mar 7, 2019
@KamilSzot
Copy link

KamilSzot commented Nov 2, 2022

I'd like to share a workaround for when you need constructor property to have correct type. You can just specify the type using interface.

class Thing { }

interface Thing {
    constructor: typeof Thing;
}

let a = new Thing();
let b = new a.constructor(); // compiles just fine and the b has type Thing

You can even use it to fix the constructor property for external classes:

// -- file Thing.ts
export class Thing {

}


// -- file Second.ts
import { Thing } from './Thing';

declare module './Thing' {
    interface Thing {
        constructor: typeof Thing
    }
}

let a = new Thing();
let b = new a.constructor();

I think it's bit less hacky than previously suggested workaround:

class Thing {
    ['constructor']: typeof Thing;
}

that can in some circumstances produce following JavaScript code that sets constructor property to undefined:

class Thing {
    ['constructor'];
}

However my workaround is still not perfect because it doesn't work with inheritance if constructor parameters in base and derived classes don't match.

class A { 
    constructor() {}
}
interface A {
    constructor: typeof A;
}

class B extends A {                                     // error here
    constructor(x: number) { super(); }
}
interface B {                                            // and here
    constructor: typeof B;
}

One solution to this might be widening the interface of base class when some classes derive from it like so:

class A { 
    constructor() {}
}
interface A {
    constructor: typeof A | typeof B;
}

class B extends A {                                     
    constructor(x: number) { super(); }
}
interface B {                                            
    constructor: typeof B;
}

This expresses the actual state of things since once you start inheriting you can no longer be sure that instance you got in variable that has the type of base class actually is of that base class, not of one of the derived ones so you can't be sure what you actually got in constructor property.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Help Wanted You can do this
Projects
None yet
Development

No branches or pull requests

5 participants