microsoft / microsoft/TypeScript

Suggestion: Reopen static and instance side of classes

オープン
#2,957 コメント 16 件 リアクション 30 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

In Discussion Suggestion
主要言語
Go
スター
111k
フォーク
14.3k
平均マージ
2日 4時間
マージ済み PR(30日)
132

説明

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

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

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

調査の方向性

まず、#9、#563、およびリンクされた CodePlex 作業項目で参照されている提案の例と過去の議論を読んでください。payload にはリポジトリのファイル、テスト、エントリポイントが記載されていません。完了するには、提案された partial class の規則、static と instance のマージ、decorator、および ES5 出力を実装して検証する必要があります。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
javascript, typescript
領域
compilers
issue の種類
機能追加
難易度
5/5
見積もり時間
1週間以上
活発さ
停滞
明瞭さ
おおむね明確
初心者へのやさしさ
25/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。