microsoft / microsoft/TypeScript
Suggestion: Reopen static and instance side of classes
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 111k
- Forks
- 14.3k
- Avg merge
- 2d 4h
- Merged PRs (30d)
- 132
Description
Summary
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/interfacepattern in theextendsclause 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
partialclass declaration must be preceded by a non-partialclass 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
partialclass declaration must have the same module visibility as the preceding non-partialclass declaration. - A
partialclass declaration must have the same generic type parameters (including constraints) as the non-partialclass declaration. - A
partialclass declaration cannot have anextendsclause, but may have animplementsclause. - A
partialclass declaration cannot have aconstructormember. - A
partialclass 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
partialclass declaration cannot have initializers. - A
partialclass 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 thepartialclass are applied.- NOTE: We could choose to disallow class decorators on a
partialclass.
- 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
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading the proposal examples and the prior discussions referenced in #9, #563, and the linked CodePlex work item. The payload names no repository files, tests, or entry points; done would require implementing and validating the proposed partial-class rules, static and instance merging, decorators, and ES5 output.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, typescript
- Domain
- compilers
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100