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 ofuisstring | numberis"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.)