Typescript – Dynamic value in type

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 of u is string | number is "stepOne" | "stepTwo" | "dynamicStep-1" | "dynamicStep-2"

Playground link

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.)

Playground link

Leave a Reply