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.
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:
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.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.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;
}
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.partial class declaration must have the same module visibility as the preceding non-partial class declaration.partial class declaration must have the same generic type parameters (including constraints) as the non-partial class declaration.partial class declaration cannot have an extends clause, but may have an implements clause.partial class declaration cannot have a constructor member.partial class declaration cannot have members with the same name as existing members on a class.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.
partial class.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 has also been discussed previously:
Note comments in #563
Also worth linking #9 since it covers basically the same use cases
yes, please!
Need to write up the status of this one
I like this suggestion, but would like to see it extended slightly.
Say I have a class generated from a C# class. I would like to be able to apply decorators to properties of the class without having to change the generated code.
I could see this working, if I am allowed to add an existing property to the partial class with the same type or a more general compatible type (so any would work in all cases). The property would _not_ change type.
Example:
// in Person.ts (a generated file from somewhere)
export class Person {
@key
id: number;
name: string;
}
// in PersonExtensions.ts
import { Person } from './Person';
partial class Person {
@required
name: any;
}
+1
In JS you do this by extending the prototype. While a friendlier syntax would be welcome, supporting the traditional style would be great for those who need it. TS is rumored to be a superset of JS after all 馃檪
~js
// [ts] Property 'foo' does not exist on type 'Klass'.
Klass.prototype.foo = function() { console.log("foo!"); }
new Klass().foo();
~
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.
Is there any progression on this topic? I have the same problem as @trusktr .
Random bikeshed as the original partial class proposal violates the design goal:
declare class Foo {};
interface class Foo {
staticMethod(): boolean;
};
let result = Foo.staticMethod(); // boolean
Most helpful comment
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.