Skip to content

Conversation

@frolic
Copy link

@frolic frolic commented Oct 29, 2025

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.

Copy link
Member

@mjackson mjackson left a 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.

Copy link
Member

@MichaelDeBoey MichaelDeBoey left a 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

Comment on lines +9 to 17
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
)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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
)
}

Copy link
Member

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.

@frolic frolic changed the title proposal: named generics proposal: named type parameters Oct 31, 2025
@frolic
Copy link
Author

frolic commented Oct 31, 2025

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:

We also have some unique casing rules for our TypeScript types to facilitate type-level code that can parallel its runtime implementation and be easily understood:

  • Use PascalCase for...
    • Entities/non-generic types (e.g. User, SomeData)
    • Generic types with noun names, like Array. As a rule of thumb, if your generic is named this way, all parameters have defaults.
  • Use camelCase for...
    • Generic types with verb names like inferDomain. Types named this way have at least one required parameter.
    • Parameter names, e.g. t in Array

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.

mjackson added a commit that referenced this pull request Nov 1, 2025
@mjackson
Copy link
Member

mjackson commented Nov 1, 2025

Hi @frolic, I went ahead and got a start on this in bd43a6e, just to see how it feels. I like how it reads and will likely be doing more of this in this repo from here on out, thanks for the suggestion!

I'm going to leave this open until we have done this across all packages.

@frolic
Copy link
Author

frolic commented Nov 1, 2025

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?

@mjackson
Copy link
Member

mjackson commented Nov 4, 2025

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants