Skip to content

T.constructor should be of type T #3841

Open

Description

Given

class Example {
}

The current type of Example.constructor is Function, but I feel that it should be typeof Example instead. The use case for this is as follows:

I'd like to reference the current value of an overridden static property on the current class.

In TypeScript v1.5-beta, doing this requires:

class Example {
    static someProperty = "Hello, world!";

    constructor() {
        // Output overloaded value of someProperty, if it is overloaded.
        console.log(
            (<typeof Example>this.constructor).someProperty
        );
    }
}

class SubExample {
    static someProperty = "Overloaded! Hello world!";

    someMethod() {
        console.log(
            (<typeof SubExample>this.constructor).someProperty
        );
    }
}

After this proposal, the above block could be shortened to:

class Example {
    static someProperty = "Hello, world!";

    constructor() {
        // Output overloaded value of someProperty, if it is overloaded.
        console.log(
            this.constructor.someProperty
        );
    }
}

class SubExample {
    static someProperty = "Overloaded! Hello world!";

    someMethod() {
        console.log(
            this.constructor.someProperty
        );
    }
}

This removes a cast to the current class.

Activity

  1. kitsonk commented on Sep 29, 2015

    @kitsonk
    Contributor

    Also, for clarity, as discussed in #4356 it is logical based on the ES specification to strongly type .constructor property of an instance and the following is essentially equivalent valid ways of creating instances:

    class Foo {
        foo() { console.log('bar'); }
    }
    
    let foo1 = new Foo();
    
    let foo2 = new foo1.constructor();
  2. added
    Effort: ModerateRequires experience with the TypeScript codebase, but feasible. Harder than "Effort: Casual".
    and removed on Oct 5, 2015
  3. added this to the milestone on Oct 5, 2015
  4. RyanCavanaugh commented on Oct 5, 2015

    @RyanCavanaugh
    Member

    Accepting PRs for this. Anyone interested? 😄

  5. weswigham commented on Oct 5, 2015

    @weswigham
    Member

    I spoke with Anders Hejlsberg (@ahejlsberg) about this - we could type constructor fairly well just with a change to our lib.d.ts now that we have this types (in theory):

    interface Constructor<T> {
      new (...args: any[]): T;
      prototype: T;
    }
    
    interface Object {
        constructor: Constructor<this>;
    }

    But! There are two issues - we don't instantiate this types on apparent type members (as in, anything on object) correctly right now, and there would be a performance impact in making every object have a this typed member (it's constructor member). Additionally, this method doesn't capture the arguments of the constructor of the class (or its static members), so it could stand to be improved.

  6. RyanCavanaugh commented on Oct 5, 2015

    @RyanCavanaugh
    Member

    Additionally, this method doesn't capture the arguments of the constructor of the class (or its static members),

    This was a dealbreaker for us since it wouldn't even address the problem in the OP. Wiring it up in the compiler the same way we do prototype seems necessary.

  7. jbondc commented on Oct 20, 2015

    @jbondc
    Contributor

    Can take a look, does

    1. Removing from lib.d.ts:
    interface Object {
        constructor: Function;
    
    1. inferring/adding a "constructor" property/symbol with a type to the classType seem like the right thing to do?
  8. jbondc commented on Oct 20, 2015

    @jbondc
    Contributor

    Nevermind, it's a bit more involved then I thought. Attempt here in case helpful:
    master...jbondc:this.constructor

    Think proper way involves changing ConstructorKeyword in getSymbolAtLocation()

  9. Arnavion commented on Dec 6, 2015

    @Arnavion
    Contributor

    As I mentioned in #5933, will this not be a breaking change for assigning object literals to class types?

    class Foo {
        constructor(public prop: string) { }
    }
    
    var foo: Foo = { prop: "5" };

    This is currently allowed, but with this issue fixed it should produce an error that the literal is missing the constructor property? If it didn't produce an error, then it would be allowed to call statics on Foo such as foo.constructor.bar() which would break if foo.constructor wasn't actually Foo.

  10. LPGhatguy commented on Dec 9, 2015

    @LPGhatguy
    ContributorAuthor

    Would this be possible to implement solely with the polymorphic this type that's a work in progress? Assuming typeof mechanics would work there as well, could the signature for Object.prototype.constructor be set to:

    interface Object {
        constructor: typeof this;
    }
  11. ahejlsberg commented on Dec 9, 2015

    @ahejlsberg
    Member

    The general problem we face here is that lots of valid JavaScript code has derived constructors that aren't proper subtypes of their base constructors. In other words, the derived constructors "violate" the substitution principle on the static side of the class. For example:

    class Base {
        constructor() { }  // No parameters
    }
    
    class Derived {
        constructor(x: number, y: number) { }  // x and y are required parameters
    }
    
    var factory: typeof Base = Derived;  // Error, Derived constructor signature incompatible
    var x = new factory();

    If we were to add a strongly typed constructor property to every class, then it would become an error to declare the Derived class above (we'd get the same error for the constructor property that we get for the factory variable above). Effectively we'd require all derived classes to have constructors that are compatible with (i.e. substitutable for) the base constructor. That's simply not feasible, not to mention that it would be a massive breaking change.

    In cases where you do want substitutability you can manually declare a "constructor" property. There's an example here. I'm not sure we can do much better than that.

  12. 89 remaining items

  13. thodnev commented on Oct 16, 2024

    @thodnev

    Could someone please add it to TS Milestones for one of the upcoming versions

    The issue is open for 9 years. And it deserves being addressed. Brief Stackoverflow review shows devs even started using ClassName.method notation instead of this.constructor.method thus bringing Divergent Change code smell into their projects to simply satisfy the typing

    The solution above by @jwbth seems the most clear and concise.
    It is just one line of code at the top. Not a big deal.

    But it still has to be added and class name still has to be provided in two distinct places. If someone accidentally forgets to change one of them while changing the other, this can lead to a very pleasant debugging experience.

  14. FrameMuse commented on Sep 6, 2025

    @FrameMuse

    This works for me as well, I put this to the superset class and subclasses inherit this correctly with constructor being a type of the according subset class.

    class C {
    -  declare ['constructor']: typeof C;
    +  declare ["constructor"]: typeof this;
    }

    Unfortunately, it doesn't work with Object interface :(
    Because Object is not actually being treated as a super class of other classes by default.

  15. alvaro-cuesta commented on Dec 4, 2025

    @alvaro-cuesta

    Valery Zinchenko (@FrameMuse) using your typeof this is not working for me.

    Am I missing something?

    Specifying each constructor works but it's easy to forget it's needed in the prototype chain.

  16. snarbles2 commented on Dec 4, 2025

    @snarbles2

    Valery Zinchenko (@FrameMuse) using your typeof this is not working for me.

    It confused me for a moment, but this and typeof this both yield the same type here: the instance type. typeof this does not refer to the constructor inside the class body.

  17. FrameMuse commented on Dec 4, 2025

    @FrameMuse

    @alvaro-cuest, snarbles2 Sorry for confusion, it should've been

    class C {
      declare static ["constructor"]: typeof this;
    }

    typeof this does not refer to the constructor inside the class body.

    It does refer to it when used in static methods.

  18. ExE-Boss commented on Dec 4, 2025

    @ExE-Boss
    Contributor

    Valery Zinchenko (@FrameMuse)

    class C {
    	declare static ["constructor"]: typeof this;
    }

    That’s still incorrect, the value of C.constructor is globalThis.Function; it’s new C().constructor and C.prototype.constructor that refer to the class C.


    The correct definition would be:

    class C {
    	declare static ["constructor"]: typeof globalThis.Function;
    	declare ["constructor"]: typeof C;
    }
  19. OldStarchy commented on Feb 1, 2026

    @OldStarchy

    TL:DR; example at bottom

    Constructor signatures and call signatures cannot be inherited like other members, static or otherwise.

    It is impossible to know what arguments are required for new this.constructor(...) because it could be any subclass, and there's no way to enforce a particular constructor signature on a subclass.

    The work around to this is a static factory function

    class Vec {
      static createWithXy(x: number, y: number): Vec {
        return new Vec(x, y);
      }
    
      clone() {
        this.createWithXy(this.x, this.y);
      }
    }

    This isn't a great solution since nothing is forcing subclasses to implement createWithXy, nor does it necessarily make sense for a subclass to even have such a constructor.

    Basically anything involving invoking a constructor cannot be reliably typed except for concretely.

    There are certainly cases where having a semantic factory function is useful (eg. new MyArray().filter()) but even then its easy to imagine situations where a subclass won't have a logically correct constructor to use.

    So I think we accept that new this.constructor is a bad idea unless you want confusion and type errors.

    That said; static members are easy to reason about; B inherits static members from A and typescript is already validating static members in an intuitive way.

    IMO the only correct way to type .constructor is by carrying over static members only;

    This can be emulated with Pick<typeof C, keyof typeof C> which strips constructor and call signatures from a type.

    class A {
      declare ['constructor']: Pick<typeof A, keyof typeof A>;
    
      static foo = 'bar';
    }
    
    class B extends A {
      // for the sake of the workaround, the constructor does not need to be declared at every level
    }
    
    class C extends B {
      declare ['constructor']: Pick<typeof C, keyof typeof C>;
    }
    
    const c = new C();
    
    // new c.constructor(); error
    console.log(c.constructor.foo);

    I hope that it shouldn't be too hard to have that kind of signature dynamically defined by the language service..

  20. ljharb commented on Mar 10, 2026

    @ljharb
    Contributor

    static members are easy to reason about

    B could also avoid inheriting static members from A (or shadow them), so it seems they're identically as difficult to reason about.

  21. OldStarchy commented on Mar 11, 2026

    @OldStarchy

    At the very least, typescript is able to provide accurate intellisense about static members, given a concrete class.

    Static members don't adhere to the same constraints as instance members. Defining an interface dictates what an instance method with that name should do, and superclasses allow you to provide default functionality to fulfill those contracts. Static methods do have inheritance but don't provide the same kind of contract adherence; You can't define static members in an interface nor create abstract static members.

    I think the problem that most people are trying to solve with T.constructor is to get access to a T factory for some purpose. We're looking for something like this as a well-known way to construct T

    interface Factory<T> {
      new (param: string, param2: number): T;
    }

    but trying to apply it to the static side of the class, for which the language has no support.

    interface DtoTranslatable<TDto, TContext> {
      toDto(): TDto;
      static fromDto(dto: TDto, context: TContext): this;
    }

    Though, I did just figure out this weird workaround

    interface DtoTranslatable<TDto, TContext, TThis> {
        toDto(): TDto;
        
        constructor: {
            fromDto(dto: TDto, context: TContext): TThis;
        }
    }
    
    class Foo implements DtoTranslatable<number, void, Foo> {
        declare ['constructor']: Pick<typeof Foo, keyof typeof Foo>;
    
        constructor(private value: number) { }
    
        toDto() {
            return this.value;
        }
    
        static fromDto(dto: number, context: void): Foo {
            return new Foo(dto);
        }
    }
    or more generically (playground)
    type ToDto<TKind extends string, TDto> = {
        [K in `to${TKind}`]: () => TDto;
    }
    type FromDtoFactory<TKind extends string, TDto, TContext, TThis> = {
        [K in `from${TKind}`]: (dto: TDto, context: TContext) => TThis;
    }
    type Static<T> = { constructor: T }
    
    type DtoTranslatable<TKind extends string, TDto, TContext, TThis> = 
        & ToDto<TKind, TDto>
        & Static<FromDtoFactory<TKind, TDto, TContext, TThis>>;
    
    interface FooDtoV1 {
        v: 1;
        n: number;
    }
    interface FooDtoV2 {
        v: 2;
        n: number;
        s: string;
    }
    type FooDto = FooDtoV2;
    
    class Foo implements DtoTranslatable<'Dto', FooDto, void, Foo>, Static<FromDtoFactory<'Dto', FooDtoV1, void, Foo>> {
        declare ['constructor']: Pick<typeof Foo, keyof typeof Foo>;
    
        constructor(private value: number, private strVal: string) { }
    
        toDto(): FooDto {
            return {
                v: 2,
                n: this.value,
                s: this.strVal,
            };
        }
    
        static fromDto(dto: FooDtoV1, context: void): Foo;
        static fromDto(dto: FooDto, context: void): Foo;
        static fromDto(dto: FooDtoV1 | FooDtoV2, context: void): Foo {
            switch (dto.v) {
                case 1: return new Foo(dto.v, '');
                case 2: return new Foo(dto.v, dto.s);
            }
        }
    }
    
    const dtoReader: FromDtoFactory<'Dto', FooDtoV1, void, Foo> = Foo;
    
    dtoReader.fromDto({v: 1, n: 4});
  22. mkahvi commented on Jul 25, 2026

    @mkahvi

    I use TS only for type augmentation and I'd love for this to be implemented, kinda tired of writing the following for every single class:

    interface MyClass {
      constructor: typeof MyClass;
    }

    Even if current behaviour might be desired by someone (who, really?), having an option that you can enable in tsconfig for assuming actual class type instead of some unrelated anonymous function would be incredibly useful, if it can't be made default behaviour.

    Though also, even if someone makes their subclass demolish the typing of the parent class as was mentioned above, this kind of type inference would still be much more accurate than the current anonymous function assumption. Such masking of parent class functionality would be breaking typing anyway, so I don't think it matters as base behaviour.

    Anyway, this typing change would be a Massive QoL improvement for me, please implement it.

  23. FrameMuse commented on Jul 25, 2026

    @FrameMuse

    We be nice to see this in TypeScript 7.0

  24. karl-police commented on Aug 21, 2026

    @karl-police

    How can this be implemented or where? What powers the type solving in TypeScript?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    In DiscussionNot yet reached consensusSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions