Skip to content

Suggestion: static constructors #265

Closed

Description

@omidkrad

A static constructor is a function that is run only once when the class is loaded. It can be used to initialize static class members and maybe as entry point to the application.

Suggested syntax is:

class MyClass {
    static initialized = false;

    static constructor() {
        MyClass.initialized = true;
    }
}

alert(MyClass.initialized);

That would be same as the following:

class MyClass {
    static initialized = false;

    static __ctor = (() => {
        MyClass.initialized = true;
    })();
}

alert(MyClass.initialized);

Except that __ctor does not need to be assigned to the class in the output code, and only needs to be invoked in an anonymous function. Also compiler can check that no more than one static constructor is defined in a class.

Update: Since this generates an Immediately-Invoked Function Expression (IIFE), compiler should make sure to move it to the end of the class definition in the output JavaScript.

TypeScript:

class MyClass {
    static constructor() {
        MyClass.initialized = true;
    }

    static initialized = false;
}

Expected JavaScript output:

var MyClass = (function () {
    function MyClass() {
    }
    MyClass.initialized = false;

    // run last
    (function () {
        MyClass.initialized = true;
    })();
    return MyClass;
})();

Note that body of the static constructor is moved to the bottom and invoked.

Related suggestion in codeplex: Please add static constructors to classes

Activity

  1. basarat commented on Jul 27, 2014

    @basarat
    Contributor

    👍

  2. ahejlsberg commented on Jul 27, 2014

    @ahejlsberg
    Member

    You can already accomplish this with a merged class/module:

    class MyClass {
        static initialize() {
            // Initialization
        }
    }
    module MyClass {
        MyClass.initialize();
    }

    Or, for that matter, you can just have the call to initialize immediately follow the class declaration:

    class MyClass {
        static initialize() {
            // Initialization
        }
    }
    MyClass.initialize();
  3. omidkrad commented on Jul 27, 2014

    @omidkrad
    Author

    Pleasure hearing from Mr. Hejlsberg...

    The only limitation I see there is that you can't make it private, whereas in my example you can :)

  4. knazeri commented on Jul 27, 2014

    @knazeri

    👍 for class/module merge suggestion

  5. NoelAbrahams commented on Jul 27, 2014

    @NoelAbrahams

    The merged class/module code requires that the developer remember to call MyClass.initialise().

    The suggestion I believe is that given the following:

    class MyClass {
        static initialized = false;
    
        static constructor() {
            MyClass.initialized = true;
        }
    }

    We get JavaScript

    var MyClass = (function () {
        function MyClass() {
        }
        MyClass.initialized = false;
    
        MyClass.__ctor = (function () {
            MyClass.initialized = true;
        })();
        return MyClass;
    })();
  6. omidkrad commented on Jul 27, 2014

    @omidkrad
    Author

    Noel Abrahams (@NoelAbrahams) Yes, good point about having to remember to call it in the merged class/module pattern.

    However, Anders Hejlsberg (@ahejlsberg)'s code has the benefit that call to the static method is made at the very end when the class is completely loaded. I updated the suggestion above to reflect that. The static constructor syntax should only generate an anonymous function invocation at the end of the JavaScript class output.

  7. DanielRosenwasser commented on Jul 27, 2014

    @DanielRosenwasser
    Member

    Privacy is a good point here, but it's worth pointing out that you can be a little "evil" to achieve the same goal without having to modify the language. ;)

    class C {
        private static initialize(): void {
            // do stuff
            C.initialize = undefined;
        }
    }
    (<any>C).initialize();

    You can even replace C.initialize = undefined with delete C.initialize if you're afraid someone will iterate over your class in a for..of loop.

    But yes, here the user is still responsible with calling initialize.

  8. omidkrad commented on Jul 27, 2014

    @omidkrad
    Author

    The suggested static constructor syntax remains to be superior IMO and has best of everything: 😌

    • Does not pollute the class structure (even better than private)
    • Does not need the developer to remember to call it
    • Makes sure it is invoked last
    • Makes sure a class does not have multiple static constructors
    • Provides a convention, and
    • It is concise and intuitive
  9. RyanCavanaugh commented on Jul 28, 2014

    @RyanCavanaugh
    Member

    Closing as Too Complex - we would need a more compelling use case since module merging provides a very close approximation to the desired behavior.

  10. 8 remaining items

  11. danquirk commented on May 27, 2015

    @danquirk
    Member

    nin-jin (@nin-jin) You can use this in static member functions and accessors, just not member initializers.

    Note that in any comparison to other languages you need to consider the somewhat unique constraints TypeScript operates with (as a superset of JavaScript and emitting clean, idiomatic JavaScript with minimal or no strange rewriting). Were we designing in a green field with only our own thoughts the language would look very different :)

  12. felixfbecker commented on May 12, 2016

    @felixfbecker
    Contributor

    But what about inheritance?

    class A {
      private options: {hello: number}
      static constructor() {
        console.log('called')
        this.options = {hello: 123}
      }
    }
    
    class B extends A {
    }

    I would expect called to be logged twice.

  13. PascalSenn commented on Jun 14, 2016

    @PascalSenn

    Felix Becker (@felixfbecker) Every static property is bind to the class. It cannot be inherited. So the static constructor should also follow this pattern to ensure a equal behavior.

  14. felixfbecker commented on Jun 14, 2016

    @felixfbecker
    Contributor

    PascalSenn static properties are inherited in ES6 and TypeScript.

  15. PascalSenn commented on Jun 14, 2016

    @PascalSenn

    Felix Becker (@felixfbecker) Thats new for me. Didnt know that. C# doesnt support it, so i thought Typescript neither.

  16. felixfbecker commented on Jun 14, 2016

    @felixfbecker
    Contributor

    PascalSenn you can even do it in ES5:

    function SuperClass {}
    SuperClass.staticProperty = 123;
    function SubClass {}
    Object.setPrototypeOf(SubClass, SuperClass);
  17. mark-norgate commented on Sep 14, 2016

    @mark-norgate

    Ryan Cavanaugh (@RyanCavanaugh) I don't think the merged module solution is anywhere near an approximation to a static constructor. It is still required that you call the static member whether you use merged modules or not. A static constructor would initialise automatically.

  18. rjamesnw commented on Apr 28, 2017

    @rjamesnw

    It's too bad generic type references were dropped in static functions. I was using some static constructor style methods on some classes (similar to JoshMcCullough's post above) and now it's all broken. The only workaround was to convert the private static constructor methods to private instance methods and call them instead via the prototype from another static constructor method (or directly after the class). I didn't want to duplicate my class generic signature on my static method as well, that's just getting too silly.

  19. Alphapage commented on Sep 30, 2017

    @Alphapage

    Hello,

    I hope Typescript will implement static constructors like this:
    sirisian/ecmascript-static-constructor#1

    It would solve "function properties" typing problem too: https://stackoverflow.com/questions/12766528/build-a-function-object-with-properties-in-typescript

  20. rjamesnw commented on Oct 27, 2017

    @rjamesnw

    Alphapage (@Alphapage) TypeScript supports many coming ES6+ changes today, best it can, and targeting such code will output code like this class MyClass { } - which works in non-IE browsers. ;) However, what you are suggesting - to support an invokable 'MyClass()' constructor - is an error since, currently, ES6 introduces a new non-invokable function type (for classes). The minute someone targets ES6+, their code would fail. ;) The spec would need to be changed regarding non-callable class functions in upcoming approval stages at some ECMAScript proposal level before this was even considered.

    BTW: There's already a proposal here as well: https://github.com/tc39/ecma262/blob/master/workingdocs/callconstructor.md

  21. gmurray81 commented on Jan 27, 2018

    @gmurray81

    I'm not sure of the exact mechanics of some of the tree shakers out there, but won't some of these strategies disrupt the tree shakers? Calling a static method on the Type as a separate action outside of the class's IIFE (in ES5) may be seen as a reference to the type? Or containing some side effect that may not be possible to omit? Presumably being able to wrap some code inside of the class's ES5 iffe would been seen as more atomic with the definition of the type?

  22. nin-jin commented on Jan 27, 2018

    @nin-jin

    gmurray81 tree shaking is partial workaround of autoloaders absence. Better to use bundler that supports autoloading. MAM in example.

  23. gmurray81 commented on Jan 27, 2018

    @gmurray81

    nin-jin (@nin-jin) even if you could just persuade everyone to use different technology in their projects to avoid a problem (some of us need to write library code for others to consume) there would still be an argument that it should be easy to make the creation/calling of the static cons atomic with the class creation.

    It seems like shoving an IIFE on the last defined static property of a type, as suggested above, might be the closest you can come with the syntax as it stands. It's a little ugly, though, comparatively.

  24. Misaka-0x447f commented on Feb 14, 2018

    @Misaka-0x447f

    Aymeric Beaumet (@aymericbeaumet) The solution you provided may caused inspector report a 'unused variable' warning which cannot be silenced in webstorm... (I cannot write better one until now. Thinking.)

  25. locked and limited conversation to collaborators on Jun 25, 2018
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

    DeclinedThe issue was declined as something which matches the TypeScript visionSuggestionAn idea for TypeScriptToo ComplexAn issue which adding support for may be too complex for the value it adds

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions