-
Notifications
You must be signed in to change notification settings - Fork 2.7k
proposal: named type parameters #10805
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
mjackson
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall I like how this reads.
I admit @pcattori had some code that looked a lot like this in the complex parser types in the route-pattern package, but I didn't like it and renamed everything to use single capital letters. However, I think I'm coming around.
The thing I like most about it is when I'm scanning a complex type, I don't have to go back to the generics list to remind myself what R means. If it's called route, I have a much clearer idea already.
My main concern is that converting all packages in this monorepo to use this style at this point is no small task. I'm happy for you to take it on, and I can support you in getting things reviewed/merged. But it's a big task (we have a lot of types!) and I just want to make sure you're up for it.
MichaelDeBoey
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this proposal @frolic!
I would however, make sure all types start with a capital letter instead of a lowercase letter.
In my experience that makes the types even more pop out and makes it more clear it's not a variable, which is in my experience mostly the case with a lowercase first letter
| export type RouteHandlers<routeMap extends RouteMap> = | ||
| | RouteHandlersWithMiddleware<routeMap> | ||
| | { | ||
| [K in keyof T]: ( | ||
| T[K] extends Route<infer M, infer P> ? RouteHandler<M, P> : | ||
| T[K] extends RouteMap ? RouteHandlers<T[K]> : | ||
| [name in keyof routeMap]: ( | ||
| routeMap[name] extends Route<infer method, infer pattern> ? RouteHandler<method, pattern> : | ||
| routeMap[name] extends RouteMap ? RouteHandlers<routeMap[name]> : | ||
| never | ||
| ) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| export type RouteHandlers<routeMap extends RouteMap> = | |
| | RouteHandlersWithMiddleware<routeMap> | |
| | { | |
| [K in keyof T]: ( | |
| T[K] extends Route<infer M, infer P> ? RouteHandler<M, P> : | |
| T[K] extends RouteMap ? RouteHandlers<T[K]> : | |
| [name in keyof routeMap]: ( | |
| routeMap[name] extends Route<infer method, infer pattern> ? RouteHandler<method, pattern> : | |
| routeMap[name] extends RouteMap ? RouteHandlers<routeMap[name]> : | |
| never | |
| ) | |
| } | |
| export type RouteHandlers<TRouteMap extends RouteMap> = | |
| | RouteHandlersWithMiddleware<TRouteMap> | |
| | { | |
| [Key in keyof TRouteMap]: ( | |
| TRouteMap[Key] extends Route<infer Method, infer Pattern> ? RouteHandler<Method, Pattern> : | |
| TRouteMap[Key] extends RouteMap ? RouteHandlers<TRouteMap[Key]> : | |
| never | |
| ) | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like this as much as routeMap. TRouteMap is an awkward name.
The only reason people put a T in front of it is so that the name of the generic doesn't match the name of the interface it extends in the constraint. But if we follow the practice that all our interfaces/types are declared with uppercase names, and all our generics are declared with lowercase names, we will never have this problem.
|
Oops, just a point of clarity: I meant type parameter names (the things that go into generics), not the names of the generics themselves. I generally follow the ArkType's guidelines:
The important bit for me is the parameter names being lowercase. And fortunately this has no downstream impacts in terms of breaking APIs, it's all internal, so things can be progressively migrated. |
|
Looks great! Already much more readable 🎉 I'm still happy to contribute some effort here. Are there any packages you think would good for me to tackle? Maybe some of the smaller ones? |
The next package I'd take a look at is route-pattern. It includes a parser for route patterns written in TypeScript that I think could benefit from some more readable generic names. |
I've spent a lot of time over the last couple years deep in strongly typed libraries with the legendary David Blass. One pattern of his that I've adopted is naming type parameters of generics like you would variable names (because that's effectively what they are). It felt weird/ugly to me at first, but after using it for a couple years, I find it much more readable, especially as types get more complex (e.g. >2 parameters in play) and while doing type transformations like mapped types.
I've mocked this up in a couple of files to demonstrate both the pure types side as well as how the type parameters are used within methods and how they nicely align with parameter names.
If ya'll are open to this, I'm happy to finish converting the rest of this package here, and will open up separate PRs for other packages that need this.