2

I've been trying to create a function where the function's parameter is a union made from two other unions. I want it so that depending on the string passed into the parameter, a different function fires accordingly. Something like this:

type Type1 = "a" | "b" | "c";
type Type2 = "x" | "y" | "z";

function doSomething(param: Type1 | Type2) {
    // if param is a part of the "Type1" union
         functionForTypeOne();
}

doSomething("z");

I searched through the internet and the docs and I couldn't find a way to do this runtime type checking. I found some "solutions" for type checking, but they were all written for object types, not strings and unions.

In basic javascript, I could create a const array and check if the string is found inside the array, like so:

const type1 = ["a", "b", "c"];
const type2 = ["x", "y", "z"];

function doSomething(param) {
    if (type1.includes(param))
        functionForTypeOne();
}

Doing this in TypeScript is kind of a solution, but since I need to create unions from these consts, it feels very inefficient, and doesn't allow me to use the type checking features of the language. I was curious if anyone has a solution for this that I possibly overlooked, or if this is the only way for me to get around my problem.

Sphygmus
  • 23
  • 3

1 Answers1

3

You can build the unions from your tuples almost automatically, and use a type guard predicate to recover type safety:

const type1 = ["a", "b", "c"] as const; // Assert as const to infer tuple instead of array string[]
const type2 = ["x", "y", "z"] as const;

// Automatically get union type from tuple values
type Type1 = typeof type1[number];
//   ^? "a" | "b" | "c"
type Type2 = typeof type2[number];
//   ^? "x" | "y" | "z"

// Type guard predicate
function isIn<T extends readonly string[]>(val: string, arr: T): val is T[number] {
    return arr.includes(val);
}

function doSomething(param: Type1 | Type2) {
    param
    //^? "a" | "b" | "c" | "x" | "y" | "z"
    if (isIn(param, type1)) {
        param
        //^? "a" | "b" | "c"
        //functionForTypeOne();
    }
}

Playground Link

ghybs
  • 47,565
  • 6
  • 74
  • 99
  • I was using a similar solution but without the "isIn" function that you provided. Thanks for your help! – Sphygmus Aug 25 '22 at 04:19