Home > database >  TypeScript check if variable is a tuple or array of tuples
TypeScript check if variable is a tuple or array of tuples

Time:07-18

Given the following code for example purposes only:

type tuple = [key: string, value: number];

function foo(arg: tuple | Array<tuple>) {
  const arr = Array.isArray(arg[0]) ? arg : [arg];
  // The type of arr is: tuple | (tuple | tuple[])[]
  console.log(arr.length);
}

foo(['key', 111]);
foo([['key', 222]]);

I've also tried typeof arg[0] === 'string' ? [arg] : arg but that also results in the same result. Running the code has the expected outcome, that arr is always an array of tuples, but TypeScript doesn't "know" it.

How can I get typescript to correctly narrow the type of arr to be tuple[] without asserting it explicitly?

CodePudding user response:

The compiler only sees that you're assigning either arg or [arg] to arr. It doesn't change what it thinks the type of arg is based on Array.isArray(arg[0]), so arr's type is the union of the types of arg and [arg]. The type of arg is tuple | tuple[] so the type of [arg] is (tuple | tuple[])[]; the union of those types simplifies to tuple | (tuple | tuple[])[] since tuple[] is a subset of (tuple | tuple[])[].

You can wrap Array.isArray in a user-defined type guard to have the compiler interpret things the way you want.

function isArrayOfTuples(arg: tuple | tuple[]): arg is tuple[] {
    return Array.isArray(arg[0]);
}

function foo(arg: tuple | tuple[]) {
    const arr: tuple[] = isArrayOfTuples(arg) ? arg : [arg];
    console.log(arr.length);
}

Otherwise I think you will have to assert the types explicitly to do this.

const arr: tuple[] = Array.isArray(arg[0]) ? arg as tuple[] : [arg] as tuple[];

Another option which may be simpler to implement in general is to change the type of arg to just tuple[]. Callers who want to pass a single tuple can wrap the argument in an array.

  • Related