microsoft / microsoft/TypeScript

Proposal: Make base class expressions able to access class type parameters

オープン
#36,406 コメント 6 件 リアクション 13 件 担当者 0 名 GitHub で見る

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

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

説明

Search Terms

Base class expressions cannot reference class type parameters

Suggestion

Right now it is possible in Ts to generate base classes dynamically (via a function):

function prop<T>(): T {
  // implementation detail doesn't matter
  return null as any;
}

function createDynamicClass<T>(_props: T): { new(): T} {
  // implementation detail doesn't matter
  return null as any;
}

// without generics on the main class all is fine
class C2 extends createDynamicClass({x: prop<number>()}) {
  setX(x: number) {
    this.x = x
  }
}

const c2 = new C2()
c2.x = 20
c2.setX(30)

And it is also possible to reuse a generic when the base class is a "pure" class with a generic type:

class Base<T> { x: number }
class Child<T> extends Base<T> {}

However, currently it is impossible for a function that generates a dynamic class to re-use the type from the child class:

// error: Base class expressions cannot reference class type parameters
class C3<T> extends createDynamicClass({x: prop<T>()}) {
  setX(x: T) {
    this.x = x
  }
}

const c3 = new C3<number>()
c3.x = 20
c3.setX(30)

There's a (somehow ugly) workaround, which is encapsulating the generation of the child class in a function and use its result:

function generateC4<T>() {
  return class extends createDynamicClass({ x: prop<T>() }) {
    setX(x: T) {
      this.x = x
    }
  }
}

const C4 = generateC4<number>()
const c4 = new C4()
c4.x = 20
c4.setX(30)

But it is quite ugly, and results in a new class being generated for each generic when it is not actually needed (just for the typings).

The proposal is basically to lift this constraint and allow it to be used (like it can be easily used for pure classes).

Use Cases

Right now in mobx-keystone it uses such a pattern (a function that generates a dynamic base class) to generate models:

  class Point extends Model({
    x: prop<number>(),
    y: prop<number>(),
  }) {
    @modelAction
    setXY(x: number y: number) {
      this.x = x
      this.y = y
    }
  }

const numberPoint = new Point({x: 10, y: 20})

but in order to support generics, rather than just doing this:

  class Point<T> extends Model({
    x: prop<T>(),
    y: prop<T>(),
  }) {
    @modelAction
    setXY(x: T, y: T) {
      this.x = x
      this.y = y
    }
  }

const numberPoint = new Point<number>({x: 10, y: 20})

users have to resort to a factory pattern like this:

function createPointClass<T>() {
  class Point extends Model({
    x: prop<T>(),
    y: prop<T>(),
  }) {
    @modelAction
    setXY(x: T, y: T) {
      this.x = x
      this.y = y
    }
  }
  return Point
}
const NumberPoint = createPointClass<number>()
const numberPoint = new NumberPoint({x: 10, y: 20})

which is far from ideal (and this is just to get typings right).

Examples

See above

Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code

It wouldn't since it is lifting a constraint that currently is just not allowed, so no current code can be using it.

  • This wouldn't change the runtime behavior of existing JavaScript code

The runtime behaviour would be exactly the same, it just addresses a typings problem.

  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.

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

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

はじめの一歩

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

調査の方向性

まず C3 と Point の例を再現し、動作する C4 ファクトリパターンと比較します。TypeScript が基底クラス式でクラスの型パラメーターをどのように扱うかを追跡し、提案された形式が型チェックを通過し、既存のランタイム動作を維持することを示すテストを定義します。

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

評価

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

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

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