microsoft / microsoft/TypeScript

generics in constructor

オープン
#62,992 コメント 4 件 リアクション 1 件 担当者 0 名 GitHub で見る

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

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

説明

🔍 Search Terms
  • "generics in constructor"
  • "generics"
  • "constructor"
  • "private class generics"
  • "private generics"
✅ Viability Checklist
⭐ Suggestion

I'd like to know if we can add the capability to have generics in constructors ; after all they are functions like any other, and some parameters of constructor could require generics to extend a type for any reason.

type Foo = {
  foo: string
}

class Bar {
  constructor<T extends Foo>(foo: T) {
    // do something with foo: T shape
  }
}
📃 Motivating Example

My real-life use-case is to implement a base repository class, which would need a property typed when calling super() for which i need to keep type integrity, therefore i'd need to be able to use extends in constructor:

// ↓ types to understand why need type integrity (→ iterate on keys)
type SimpleProjection<T> = {
  [K in keyof T]?: boolean
}

type FilterKeysWhenEquals<T extends object, V> = keyof {
  [K in keyof T as T[K] extends V ? K : never]: T[K]
}

type FilterKeysWhenDiffers<T extends object, V> = keyof {
  [K in keyof T as T[K] extends V ? never : K]: T[K]
}

type HasFalse<T extends object> = T[keyof T] extends false ? true : false

type Projected<
  Schema extends object,
  Projection extends object | undefined,
  DefaultProjection extends object | undefined,
> = Projection extends undefined
  ? Schema
  : HasFalse<Projection & {}> extends true
    ? // Exclusion mode
      Omit<Schema, FilterKeysWhenEquals<(Projection & {}) | (DefaultProjection & {}), false>>
    : // Inclusion mode
      Pick<Schema, FilterKeysWhenDiffers<Projection & {}, false>>

// ↓ said class
export class Repository<
  Schema extends object,
  DefaultProjection extends ExclusionProjection<Schema> | undefined = undefined,
> {
  constructor(
    protected model: Model<Schema>,
    protected projection?: DefaultProjection,
  ) {}

  async findById<Projection extends SimpleProjection<Schema> | undefined = undefined>(
    id: ID,
    projection?: Projection,
  ) {
    return this.model
      .findById(id, this.mergeProjection(projection))
      .lean<Projected<Schema, Projection, DefaultProjection>>({ transform })
  }
}

// ↓ current usage
const EXCLUDE_DEFAULTS = { passkeys: false, password: false, salt: false } as const

export class UserRepository extends Repository<User, typeof EXCLUDE_DEFAULTS> {
  constructor() {
    super(new UserModel(), EXCLUDE_DEFAULTS)
  }
}

but if i could have generics in constructor, i would be able to write:

// ↓ one generic at class level, the 2nd one in constructor
export class Repository<Schema extends object> {
  constructor<
    DefaultProjection extends ExclusionProjection<Schema> | undefined = undefined,
  >(
    protected model: Model<Schema>,
    protected projection?: DefaultProjection,
  ) {}

  async findById<Projection extends SimpleProjection<Schema> | undefined = undefined>(
    id: ID,
    projection?: Projection,
  ) {
    return this.model
      .findById(id, this.mergeProjection(projection))
      .lean<Projected<Schema, Projection, typeof this.projection>>({ transform })
  }
}

// ↓ can inline the object directly, no need to create a const just so that i can pass `typeof theConst` in 2nd generics
export class UserRepository extends Repository<User> {
  constructor() {
    super(new UserModel(), { passkeys: false, password: false, salt: false })
  }
}
💻 Use Cases
  1. What do you want to use this for? Have simpler syntax when writing constructors

  2. What shortcomings exist with current approaches? Need to complicate class generics and merge them with constructor generics (which could be 2 different scopes sometimes)

  3. What workarounds are you using in the meantime? manual mode. verbose and need specific structure, but acceptable enough at least for my use-case.

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

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

はじめの一歩

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

調査の方向性

実装ファイルやテストは指定されていません。まず、提案されているコンストラクターの型パラメーター構文と、issue にある既存のクラスおよびメソッドのジェネリックの例を確認し、その後、TypeScript がコンストラクターシグネチャと型推論をどのように処理するかを追ってください。完了とするには、合意された設計と、記載された JavaScript 出力の制約を維持する対応するコンパイラーの動作が必要です。

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

評価

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

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

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