microsoft / microsoft/TypeScript
Proposal: Make base class expressions able to access class type parameters
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
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.
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
Commencez par reproduire les exemples C3 et Point et comparez-les au modèle de fabrique C4 fonctionnel. Suivez la manière dont TypeScript gère les paramètres de type des classes dans les expressions de classes de base, puis définissez des tests montrant que les formes proposées passent la vérification des types tout en préservant le comportement existant à l’exécution.
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
- 30/100