Description
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:
- Declaring abstract static methods in an abstract class should not affect the representation of the abstract class in the JavaScript code.
- 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
orstatic
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 andstatic
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.
- What is the grammar of this feature? - The grammar of this feature is
- Semantic
- What is an error under the proposed feature? - Now there are errors compiling
abstract static
modifier of an abstract class method andstatic
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.
- What is an error under the proposed feature? - Now there are errors compiling
- 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.