I’ve also run into the issue of not being able to express errors in the usage of the types; sometimes never is exactly what you want!
I wrote a type-level path parameters parser where PathParamsOf<"/users/:userId/books/:bookId"> returns "userId" | "bookId", but if the path is valid and there’s no path params the result should be never because then I want to use the result as the key of an object, and if the key is never the object will be {}. But then if I want to signal that the path is invalid I have to use some sentinel value like null, which is confusing for the user of the API and doesn’t explain anything about the error, and to me it feels like checking for return codes in C.
I didn’t know about the throw types proposal but it looks exactly like what I need!
How do you do that sort of string manipulation at the type level in Typescript?
e: Oh, I guess it’s similar to this trick where you can use extends as a sort of parser.
I really like TypeScript’s type system a lot, and I find myself wishing I could do this sort of cool record-type-y and string-manipulation-y stuff in… basically every other language.
Yeah, there’s an IsPathParam type which just parses a single param by checking if it extends ":${infer Name}", and then the path parser itself that checks for more than two path items ("/${infer Item}/${infer Rest}") and uses IsPathParam to parse the item to the param name or never and recurses on the res, just one item ("/${infer LastItem}") and otherwise returns null. Then I had to make a wrapper type that check if the result of this last one extends null and just returns null for the whole type.
I also love the flexibility of the type system, but I’m not sure I’d want this in every language. On one hand type-level computations are just a lot of fun, but on the other this flexibility emerges from TypeScript’s need to make ergonomic types for an already existing language with a lot of baggage and existing idioms, and I feel like they’re not as needed in languages where you have other expressive features like custom infix operators and other things. The horrors I come up with aren’t necessarily the intended use of the type system.
I’ve also run into the issue of not being able to express errors in the usage of the types; sometimes
never
is exactly what you want!I wrote a type-level path parameters parser where
PathParamsOf<"/users/:userId/books/:bookId">
returns"userId" | "bookId"
, but if the path is valid and there’s no path params the result should benever
because then I want to use the result as the key of an object, and if the key isnever
the object will be{}
. But then if I want to signal that the path is invalid I have to use some sentinel value likenull
, which is confusing for the user of the API and doesn’t explain anything about the error, and to me it feels like checking for return codes in C.I didn’t know about the throw types proposal but it looks exactly like what I need!
How do you do that sort of string manipulation at the type level in Typescript?
e: Oh, I guess it’s similar to this trick where you can use
extends
as a sort of parser.I really like TypeScript’s type system a lot, and I find myself wishing I could do this sort of cool record-type-y and string-manipulation-y stuff in… basically every other language.
Yeah, there’s an
IsPathParam
type which just parses a single param by checking if it extends":${infer Name}"
, and then the path parser itself that checks for more than two path items ("/${infer Item}/${infer Rest}"
) and usesIsPathParam
to parse the item to the param name or never and recurses on the res, just one item ("/${infer LastItem}"
) and otherwise returnsnull
. Then I had to make a wrapper type that check if the result of this last one extendsnull
and just returnsnull
for the whole type.I also love the flexibility of the type system, but I’m not sure I’d want this in every language. On one hand type-level computations are just a lot of fun, but on the other this flexibility emerges from TypeScript’s need to make ergonomic types for an already existing language with a lot of baggage and existing idioms, and I feel like they’re not as needed in languages where you have other expressive features like custom infix operators and other things. The horrors I come up with aren’t necessarily the intended use of the type system.