I'm trying to figure out how to return an inferred parameter type for the purposes of a chaining API. Here is a simplified example:
type NoInfer<T> = [T][T extends unknown ? 0 : never]
interface Alpha<Foo extends string> {
foo: Foo
bar: `Depends on ${NoInfer<Foo>}`
}
interface Chain<ACC> {
bravo: <T extends { [K in keyof T]: string }>(xs: { [K in keyof T]: Alpha<T[K]> }) => void
// instead of void, return Chain<[***inferred type of xs***, ...ACC> -------------^^^^
}
In the above, bravo parameter type works well but the ability to build up type context via a chain is missing. How would the inferred type of xs on every call to bravo be captured in the return type and passed recursively to the generic type Chain?
Note I have discovered that making the return type be { [K in keyof T]: Alpha<T[K]> } seems to work. TS seems to have behaviour such that the given input (parameter arguments) will determine what this type in return position becomes.
However, the outright duplication is at best quite hard to read, maintain, etc. Is there a solution that doesn't go down this path?
What you really want is to create inline type aliases, as described in microsoft/TypeScript#30979. Unfortunately that feature is not part of the language. If it were, then maybe I could tell you to write
and move on. Without such a feature, all I can give you are workarounds.
The most generally applicable workaround is to just create a utility type; that is, regular type alias external to the place you need it:
And then use it multiple times where you need it:
This requires a little more code than you might want, but it's obvious what you're doing. Also, if the type is something you actually do need multiple times, it might be worthwhile to give it a meaningful name (
MapAlphafor something that mapsAlphaover an object type seems reasonable to me).As a second approach: if there's a value of the type you want in scope, you can use the
typeofoperator to refer to its type. This will work even if the value is a function parameter:Here
typeof xsis the same as{ [K in keyof T]: Alpha<T[K]> }.This is terser, but might be a little more confusing to readers. But the main issue is that it's not always possible to find a value directly with the type you want, and you might have to fall back to a utility type definition.
There are sometimes other workarounds involving using extra type parameters in a wider scope to act as an inline type alias, but for the example as given I couldn't come up with anything I'd want to suggest. I mean, the following sort of works:
But it relies on
Ubeing inferred fromxsseparately fromT, and the types are therefore not necessarily identical. I'd almost always recommend creating a utility type before playing games with inference like this.Playground link to code