Repository: TypeScript
Issue: Suggestion: Reopen static and instance side of classes
Labels: Suggestion,In Discussion

# 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):

``` ts
// 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:

``` ts
// 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 the `extends` 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#:

``` ts
// 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 an `extends` clause, but may have an `implements` clause.
- A `partial` class declaration cannot have a `constructor` 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 the `partial` class are applied.
  - NOTE: We could choose to disallow class decorators on a `partial` class.
# 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