microsoft / microsoft/TypeScript
Proposal: `decoratedWith` type operator and generic 'value' parameters
Personne n'a encore pris cette issue.
- Langage dominant
- Go
- Ătoiles
- 111k
- Forks
- 14.4k
- Merge moyen
- 1 j 19 h
- PR mergées (30 j)
- 117
Description
đ Search Terms
Decorators
TypeScript 5
â 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 feature would agree with the rest of our Design Goals: https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals
â Suggestion
WIP: Goal is to get this to canonical ASAP, all help appreciated.
This is a proposal introduces two things a type operator named: decoratoredWith and generic 'value' parameters.
First off, the decoratedWith type operator.
This type operator would check if a method, property/field, getter, setter, auto-accessor or class is decorated with a specific decorator function. (I think it shouldn't support legacy decorators)
To illustrate:
// Decorator functions
function EntityOnly<C, T>(_target: undefined, context: ClassFieldDecoratorContext<C, T>) {
// Does something
}
function SchemaProperty<C, T>(schema: any) {
return function(_target: undefined, context: ClassFieldDecoratorContext<C, T>) {
// Does something
}
}
// Class with some fields decorated
class User {
id: number;
@SchemaProperty({ type: string, })
username: string;
@SchemaProperty({ type: string, })
displayName: string;
@EntityOnly
hashedPasssword: any;
@EntityOnly
IBAN: string;
}
// A simple example of the decoratedWith type operator albeit this is not where this operator would shine.
type IsEntityOnly = User['hashedPassword'] decoratedWith EntityOnly ? true : false;
// Should also work with functions returning decorators.
type IsEntityOnly = User['username'] decoratedWith SchemaProperty ? true : false;
Should work the same with methods, properties/fields, getters, setters, auto-accessors and classes too.
Revision 1: Generic 'value' parameters
Disclaimer: I understand that the following might be such a substantial addition to the proposal that it should be a separate proposal but I am looking for feedback first before adding yet another proposal. Perhaps this part is already covered in another proposal, if so, great, if not then perhaps there is a reason this hasn't been proposed yet.
My initial proposal featured some code using the decoratedWith proposal with a generic type which wasn't really a type but rather a value pointing to a decorator function. (as pointed out by @jcalz) And although this is currently not possible in TypeScript I think it could be very beneficial to be able to generalize decorator functions as values. Perhaps these "generic 'value' parameters should be supported by their own group of 'value' operators which would refer to a value rather than a type. These values must be constant in nature, so would be limited to functions, const variables, enums, classes, etc. Naturally they cannot refer to types. Here's the list of 'value' operators:
valueOf- The one 'value' operator to rule them all. This 'value' operator accepts any value constant in nature.funcOf- This 'value' operator accepts functions.constOf- This 'value' operator refers to const variables.enumOf- This 'value' operator refers to enums.classOf- This 'value' operator refers to classes.
These new operators could streamline some processes like referring to an actual class rather than just it's constructor.
Let me start by illustrating some problems that generic 'value' operators could solve on their own:
The first problem would be that you can't enforce (at least not in a simple way as far as my knowledge goes, any examples that contradict this are appreciated as I could use that to enhance my proposal) a generic type parameter to be a class. The problem illustrated:
type ClassType<T> = new () => T;
// This user class takes a generic type parameter.
// The constructor has a parameter which requires a value with a constructor returning type T.
// Yet you cannot demand the generic parameter to be a class.
class User<T> {
private _classType: ClassType<T>;
constructor(classType: ClassType<T>) {
this._classType = classType;
}
}
interface Entity {
}
class Item implements Entity {
}
// As shown here, generic parameter T can be an interface while you might to enforce the type being a class.
new User<Entity>(Item);
What my proposal would do is, it would enable users to do the following:
class User<classOf T> {
private _classType = T;
}
interface Entity {
}
class Item extends Entity {
}
// This shouldn't work because Entity is not a class.
new User<Entity>();
// This should work because Item is a class.
new User<Item>();
Here the 'value' operator classOf is used to denote a generic 'value' parameter (limited to class values). Generic 'value' parameters accept values rather than types and can be used throughout a class or function as value. The User class as described in the problem would translate to a class similar as that of the User class described in the previously illustrated problem which the classOf operator should solve. The only difference being that the classOf operator shortens the code and could restrict the generic parameter to actual classes ,
The other 'value' operators operate the same way where valueOf is the swiss army knife of the bunch which accepts every value accepted by all the other 'value' operators.
Generic 'value' parameters powered by 'value' operators can enable you to use constant values in type declarations.
To take my proposed decoratedWith for example:
// Together with the 'value' operators and generic 'value' parameters this is where `decoratedWith` could truly shine.
// Here it only returns the key names of the members of generic parameter T that are decorated with the decorator function provided in the generic 'value' parameter DT which must be a function.
type DecoratedMembers<T, funcOf DT> = keyof { [P in keyof T as T[P] decoratedWith DT ? P : never]: any; };
// This variable would have the union type: "hashedPassword" | "IBAN"
const entityOnlyMembers: DecoratedMembers<User, EntityOnly>;
// This variable would have the union type: "username" | "displayName"
const entityOnlyMembers: DecoratedMembers<User, SchemaProperty>;
This is all theoretically possible within TypeScript without requiring any change in the JavaScript spec. I am open for feedback. Also I am no longer sure if this is indeed not a breaking change (as it adds keywords), so if anyone could comment on that, that be great too.
đ Motivating Example
You can now work with types that are computed based on the fact whether methods, properties/fields, getters, setters, auto-accessors or classes include certain decorators or not.
đ» Use Cases
- What do you want to use this for?
To be able to act on the inclusion or exclusion of decorators on methods, properties/fields, getters, setters auto-accessors and classes.
- What shortcomings exist with current approaches?
There are not really ways to handle such cases right now.
- What workarounds are you using in the meantime?
There aren't really any workarounds right now as far as my knowledge goes.
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
Lâissue contient une proposition gĂ©nĂ©rale pour un opĂ©rateur de type decoratedWith et des paramĂštres de valeur gĂ©nĂ©riques, mais ne nomme aucun fichier dâimplĂ©mentation, point dâentrĂ©e ou test. Commencez par lire lâarchitecture du vĂ©rificateur de types et de lâanalyseur syntaxique de TypeScript, puis dĂ©terminez si la proposition peut ĂȘtre dĂ©limitĂ©e en une fonctionnalitĂ© testable indĂ©pendamment. Pour considĂ©rer le travail comme terminĂ©, il faudrait un design approuvĂ©, une implĂ©mentation et des tests du compilateur couvrant la syntaxe et le comportement des types proposĂ©s.
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é
- Ă clarifier
- Accessibilité débutants
- 20/100