microsoft / microsoft/TypeScript
generics in constructor
Personne n'a encore pris cette issue.
- Langage dominant
- Go
- Étoiles
- 111k
- Forks
- 14.3k
- Merge moyen
- 2 j 4 h
- PR mergées (30 j)
- 132
Description
🔍 Search Terms
- "generics in constructor"
- "generics"
- "constructor"
- "private class generics"
- "private generics"
✅ Viability Checklist
- This wouldn't be a breaking change in existing TypeScript/JavaScript code
- This wouldn't change the runtime behavior of existing JavaScript code
- 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, new syntax sugar for JS, etc.)
- This isn't a request to add a new utility type: https://github.com/microsoft/TypeScript/wiki/No-New-Utility-Types
- This feature would agree with the rest of our Design Goals: https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals
⭐ 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
-
What do you want to use this for? Have simpler syntax when writing constructors
-
What shortcomings exist with current approaches? Need to complicate class generics and merge them with constructor generics (which could be 2 different scopes sometimes)
-
What workarounds are you using in the meantime? manual mode. verbose and need specific structure, but acceptable enough at least for my use-case.
Guide de contribution
Ouvrir le guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Piste de recherche
Aucun fichier d’implémentation ni test n’est nommé. Commencez par examiner la syntaxe proposée des paramètres de type de constructeur ainsi que les exemples génériques existants de classes et de méthodes dans l’issue, puis suivez la manière dont TypeScript gère les signatures de constructeur et l’inférence de types. Pour considérer le travail comme terminé, il faudrait disposer d’une conception approuvée et d’un comportement correspondant du compilateur qui préserve les contraintes indiquées pour la sortie JavaScript.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Évaluation
- Stack technique
- typescript
- Domaine
- compilers
- Type d'issue
- Fonctionnalité
- Difficulté
- 5/5
- Temps estimé
- Plus d'une semaine
- Activité
- À l'abandon
- Clarté
- Plutôt claire
- Accessibilité débutants
- 35/100