Skip to content

Suggestion: Add abstract static methods in classes and static methods in interfaces #14600

Closed
@vyshkant

Description

@vyshkant

As a continuation #2947 issue, which allows the abstract modifier on method declarations but disallows it on static methods declarations, I suggest to expand this functionality to static methods declarations by allowing abstract static modifier on method declarations.

The related problem concerns static modifier on interface methods declaration, which is disallowed.

1. The problem

1.1. Abstract static methods in abstract classes

In some cases of using the abstract class and its implementations I may need to have some class-dependent (not instance-dependent) values, that shoul be accessed within the context of the child class (not within the context of an object), without creating an object. The feature that allows doing this is the static modifier on the method declaration.

For example (example 1):

abstract class AbstractParentClass {
}

class FirstChildClass extends AbstractParentClass {

    public static getSomeClassDependentValue(): string {
        return 'Some class-dependent value of class FirstChildClass';
    }
}

class SecondChildClass extends AbstractParentClass {

    public static getSomeClassDependentValue(): string {
        return 'Some class-dependent value of class SecondChildClass';
    }
}

FirstChildClass.getSomeClassDependentValue(); // returns 'Some class-dependent value of class FirstChildClass'
SecondChildClass.getSomeClassDependentValue(); // returns 'Some class-dependent value of class SecondChildClass'

But in some cases I also need to acces this value when I only know that the accessing class is inherited from AbstractParentClass, but I don't know which specific child class I'm accessing. So I want to be sure, that every child of the AbstractParentClass has this static method.

For example (example 2):

abstract class AbstractParentClass {
}

class FirstChildClass extends AbstractParentClass {

    public static getSomeClassDependentValue(): string {
        return 'Some class-dependent value of class FirstChildClass';
    }
}

class SecondChildClass extends AbstractParentClass {

    public static getSomeClassDependentValue(): string {
        return 'Some class-dependent value of class SecondChildClass';
    }
}

abstract class AbstractParentClassFactory {

    public static getClasses(): (typeof AbstractParentClass)[] {
        return [
            FirstChildClass,
            SecondChildClass
        ];
    }
}

var classes = AbstractParentClassFactory.getClasses(); // returns some child classes (not objects) of AbstractParentClass

for (var index in classes) {
    if (classes.hasOwnProperty(index)) {
        classes[index].getSomeClassDependentValue(); // error: Property 'getSomeClassDependentValue' does not exist on type 'typeof AbstractParentClass'.
    }
}

As a result, the compiler decides that an error occurred and displays the message: Property 'getSomeClassDependentValue' does not exist on type 'typeof AbstractParentClass'.

1.2. Static methods in interfaces

In some cases, the interface logic implies that the implementing classes must have a static method, that has the predetermined list of parameters and returns the value of exact type.

For example (example 3):

interface Serializable {
    serialize(): string;
    static deserialize(serializedValue: string): Serializable; // error: 'static' modifier cannot appear on a type member.
}

When compiling this code, an error occurs: 'static' modifier cannot appear on a type member.

2. The solution

The solution to both problems (1.1 and 1.2) is to allows the abstract modifier on static method declarations in abstract classes and the static modifier in interfaces.

3. JS implementaion

The implementation of this feature in JavaScript should be similar to the implementation of interfaces, abstract methods and static methods.

This means that:

  1. Declaring abstract static methods in an abstract class should not affect the representation of the abstract class in the JavaScript code.
  2. Declaring static methods in the interface should not affect the representation of the interface in JavaScript code (it is not present).

For example, this TypeScript code (example 4):

interface Serializable {
    serialize(): string;
    static deserialize(serializedValue: string): Serializable;
}

abstract class AbstractParentClass {
    public abstract static getSomeClassDependentValue(): string;
}

class FirstChildClass extends AbstractParentClass {

    public static getSomeClassDependentValue(): string {
        return 'Some class-dependent value of class FirstChildClass';
    }
}

class SecondChildClass extends AbstractParentClass implements Serializable {

    public serialize(): string {
        var serialisedValue: string;
        // serialization of this
        return serialisedValue;
    }

    public static deserialize(serializedValue: string): SecondChildClass {
        var instance = new SecondChildClass();
        // deserialization
        return instance;
    }

    public static getSomeClassDependentValue(): string {
        return 'Some class-dependent value of class SecondChildClass';
    }
}

should be compiled to this JS code:

var AbstractParentClass = (function () {
    function AbstractParentClass() {
    }
    return AbstractParentClass;
}());

var FirstChildClass = (function (_super) {
    __extends(FirstChildClass, _super);
    function FirstChildClass() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    FirstChildClass.getSomeClassDependentValue = function () {
        return 'Some class-dependent value of class FirstChildClass';
    };
    return FirstChildClass;
}(AbstractParentClass));

var SecondChildClass = (function (_super) {
    __extends(SecondChildClass, _super);
    function SecondChildClass() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    SecondChildClass.prototype.serialize = function () {
        var serialisedValue;
        // serialization of this
        return serialisedValue;
    };
    SecondChildClass.deserialize = function (serializedValue) {
        var instance = new SecondChildClass();
        // deserialization
        return instance;
    };
    SecondChildClass.getSomeClassDependentValue = function () {
        return 'Some class-dependent value of class SecondChildClass';
    };
    return SecondChildClass;
}(AbstractParentClass));

4. Relevant points

  • The declaration of an abstract static method of an abstract class should be marked with abstract static modifier
  • The implementation of abstract static method of an abstract class shoul be marked with abstract static or static modifier
  • The declaration of static method of an interface should be marked with static modifier
  • The implementation of static method of an interface shoul be marked with static modifier

All the other properties of abstract static modifier should be inherited from abstract and static modifiers properties.

All the other properties of static interface method modifier should be inherited from the interface methods and static modifier properties.

5. Language Feature Checklist

  • Syntactic
    • What is the grammar of this feature? - The grammar of this feature is abstract static modifier of an abstract class method and static modifier of an interface method.
    • Are there any implications for JavaScript back-compat? If so, are they sufficiently mitigated? - There is no any implications for JavaScript back-compat.
    • Does this syntax interfere with ES6 or plausible ES7 changes? - This syntax does not interfere with ES6 or plausible ES7 changes.
  • Semantic
    • What is an error under the proposed feature? - Now there are errors compiling abstract static modifier of an abstract class method and static modifier of an interface method. Proposed feature have to fix these errors.
    • How does the feature impact subtype, supertype, identity, and assignability relationships? - The feature does not impact subtype, supertype, identity, and assignability relationships.
    • How does the feature interact with generics? - The feature does not interact with generics.
  • Emit
    • What are the effects of this feature on JavaScript emit? - There are no effects of this feature on JavaScript emit.
    • Does this emit correctly in the presence of variables of type ‘any’? - Yes.
    • What are the impacts to declaration file (.d.ts) emit? - There is no impacts to declaration file.
    • Does this feature play well with external modules? - Yes.
  • Compatibility
    • Is this a breaking change from the 1.0 compiler? - Probably yes, 1.0 compiler will not be able to compile the code implementing this feature.
    • Is this a breaking change from JavaScript behavior? - No.
    • Is this an incompatible implementation of a future JavaScript (i.e. ES6/ES7/later) feature? - No.
  • Other
    • Can the feature be implemented without negatively affecting compiler performance? - Probably yes.
    • What impact does it have on tooling scenarios, such as member completion and signature help in editors? - Probably it does not have any impact of this type.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Awaiting More FeedbackThis means we'd like to hear from more people who would be helped by this featureSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions