microsoft / microsoft/TypeScript

generics in constructor

Offen
#62,992 4 Kommentare 1 Reaktion 0 zugewiesene Personen Auf GitHub ansehen

Dieses Issue hat noch niemand übernommen.

Awaiting More Feedback Suggestion
Vorherrschende Sprache
Go
Sterne
111k
Forks
14.3k
Ø Merge
2 T. 4 Std.
Gemergte PRs (30 T.)
132

Beschreibung

🔍 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.

Beitragsleitfaden

Beitragsleitfaden öffnen

Erste Schritte

  1. Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
  3. Forke das Repository und arbeite in einem Branch.
  4. Öffne einen Pull Request, der die Issue-Nummer nennt.

Rechercherichtung

Es werden keine Implementierungsdateien oder Tests genannt. Beginne damit, die vorgeschlagene Syntax für Typparameter von Konstruktoren und die vorhandenen generischen Beispiele für Klassen und Methoden im Issue zu prüfen, und verfolge anschließend, wie TypeScript Konstruktorsignaturen und Typinferenz behandelt. Für den Abschluss wären ein abgestimmtes Design und ein entsprechendes Compilerverhalten erforderlich, das die genannten Einschränkungen für die JavaScript-Ausgabe beibehält.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
typescript
Bereich
compilers
Issue-Typ
Feature
Schwierigkeit
5/5
Geschätzter Aufwand
Über eine Woche
Aktivitätsstatus
Veraltet
Klarheit
Größtenteils klar
Anfängerfreundlichkeit
35/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.