Let’s assume i have a form, and I want to create a function that returns the current step name.
First step is stepOne
, the second step is stepTwo
, and the third step is a dynamic step that changes depending on different parameters, let’s assume some kind of id. So its base name would be dynamicStep-
and it can be returned from the function as dynamicStep-123
or dynamicStep-555
for example.
I want to define the return type as accurately as possible, is there a way to do so?
something along the lines of
type FunctionReturnType = 'stepOne' | 'stepTwo' | 'dynamicStep-'+?
I know I can use interpolated strings in type declarations, but in this case I don’t have access to the id from outside the function
>Solution :
…and the third step is a dynamic step that changes depending on different parameters, let’s assume some kind of id…
If that ID’s value isn’t a compile-time constant or can’t be derived from the types (not values) of the parameters, then you can’t do this at compile-time. You’ll probably want to use "stepOne" | "stepTwo" | `dynamicStep-${number}`
instead.
But if that ID’s value is a compile-time constant and it can be derived from the types of the input parameters, you can do this with a mapped type. For example:
type FunctionReturnType<ArgType extends string | number> =
| "stepOne"
| "stepTwo"
| (
ArgType extends string
? "dynamicStep-1"
: "dynamicStep-2"
);
function example<ArgType extends string | number>(arg: ArgType): FunctionReturnType<ArgType> {
// ...
}
With that:
- The return type of
example("x")
is"stepOne" | "stepTwo" | "dynamicStep-1"
- The return type of
example(42)
is"stepOne" | "stepTwo" | "dynamicStep-2"
- The return type of
example(u)
where the type ofu
isstring | number
is"stepOne" | "stepTwo" | "dynamicStep-1" | "dynamicStep-2"
If you wanted example(u)
in the above to return a different dynamic step rather than a union of the ones for strings and numbers, you can do that by using [ArgType] extends [string]
and doing an explicit branch for number:
type FunctionReturnType<ArgType extends string | number> =
| "stepOne"
| "stepTwo"
| (
[ArgType] extends [string]
? "dynamicStep-1"
: [ArgType] extends [number]
? "dynamicStep-2"
: never
);
That makes the return type of example(u)
just "stepOne" | "stepTwo"
, it doesn’t include a union of the types for strings and numbers, by preventing the mapped type from distributing the union. (Or of course you could use "dynamicStep-3"
or whatever you like instead of never
.)