Suggestion: Reopen static and instance side of classes
See original GitHub issueSummary
To support both the semantics of subclassing built-ins in ES6 and still allow authors to augment built-ins, we need a mechanism to reopen the static and instance sides of a class.
Current state
Today we can re-open interfaces, allowing authors to augment built-ins (for example, to support polyfills):
// in lib.d.ts
interface Array<T> { /*...*/ }
interface ArrayConstructor { /*...*/ }
declare var Array: ArrayConstructor;
// in polyfill.ts
interface Array<T> {
includes(value: T): Boolean;
}
interface ArrayConstructor {
of<T>(...items: T[]): Array<T>;
}
Array.prototype.includes = function (value: any) { return this.indexOf(value) != -1; }
Array.of = function<T> (...items: T[]) { return items; }
We can also re-open the static side of a class, in a limited fashion:
// initial declaration
class MyClass {
}
// re-open
module MyClass {
export var staticProperty = 1;
}
There are several issues with these approaches:
- You cannot use type defined by the
var
/interface
pattern in theextends
clause of a class in TypeScript, meaning that “classes” defined using this pattern cannot be subclassed in ES6, which is an issue for built-ins. - While you can re-open the static side of a class using
module
, you can only use non-keyword identifiers for property names. So you could not, for example, add a[Symbol.species]
property to the class, or use decorators on these members. - There is no way to re-open the instance side of a class.
Proposal
I propose we add a new syntactic modifier for the class
declaration that would indicate we are re-opening an existing class. For this example I am using the keyword partial
, although the semantics here differ significantly than the same-named capability in C#:
// in lib.d.ts
declare class Array<T> {
}
// in polyfill.ts
partial class Array<T> {
static of<T>(...items: T[]) { return items; }
includes(value: T): boolean { return this.indexOf(value) != -1; }
}
// emit (ES5)
Array.of = function() {
var items = [];
for (var _i = 0; i < arguments.length; i++)
items[i] = arguments[i];
return items;
}
Array.prototype.includes = function(value) {
return this.indexOf(value) != -1;
}
Rules
- A
partial
class declaration must be preceded by a non-partial
class declaration in the same lexical scope. These should be the same rules that apply when merging a module with a class or function today. - A
partial
class declaration must have the same module visibility as the preceding non-partial
class declaration. - A
partial
class declaration must have the same generic type parameters (including constraints) as the non-partial
class declaration. - A
partial
class declaration cannot have anextends
clause, but may have animplements
clause. - A
partial
class declaration cannot have aconstructor
member. - A
partial
class declaration cannot have members with the same name as existing members on a class.- Exception: ambient partial class declaration members can merge with other ambient partial class declaration members if they are compatible overloads, similar to interfaces.
- Non-static property declarations on a
partial
class declaration cannot have initializers. - A
partial
class declaration can have a class decorator. User code that executes in-between the initial class declaration and the partial declaration will be able to observe the class before decorators on thepartial
class are applied.- NOTE: We could choose to disallow class decorators on a
partial
class.
- NOTE: We could choose to disallow class decorators on a
Out of scope
- This proposal does not cover the case where built-in “classes” can often also be called as functions. This case is covered in #2959.
- This proposal does not cover the case where authors may have already extended the interface of a built in. This case is covered in #2961.
Previous discussions
This has also been discussed previously:
- #9 - Suggestion: Extension methods
- #563 - Partial classes
- https://typescript.codeplex.com/workitem/100
Issue Analytics
- State:
- Created 8 years ago
- Reactions:21
- Comments:12 (6 by maintainers)
Top Results From Across the Web
Suggestion: Reopen static and instance side of classes #2957
There is no way to re-open the instance side of a class. Proposal. I propose we add a new syntactic modifier for the...
Read more >Reopening Classes and Instances - The Object Model
reopen () is used to add instance methods and properties that are shared across all instances of a class. It does not add...
Read more >How can I prevent invoking a static method on an instance of ...
Calling a static method on a variable with null value will not raise NullPointerException. Following code will print 42 even though variable ...
Read more >ABAP Static vs Instance method - Which to use when?
Static methods are methods which can be called irrespective to the class instance. You can access only static attributes and static events ...
Read more >c# - Could a singleton type replace static methods and classes?
It's important to note that true functions are perfectly fine to remain a static method. For instance, everything in the Math class is...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
I would like this, because for example, if a 3rd-party type definition is not accurate, I would like to augment the class in order to quickly fix it for my case.
In my case, the constructor of a class is stated to receive an argument of one type, but in reality it can accept an argument of a union of two types, and I’d like to simply fix this without having to fork a library or rewrite the entire definition of the class.
I think this issue is also tracking adding new overloads to the construct signature, which can’t be done with
namespace
… (although proposal in the suggestion OP does not seem to allow that either). So this should probably stay open.