How to use generics to type a zip function?

Viewed 63

I would like to add types to this JS function:

function zip(array1, array2) {
  const length = Math.min(array1.length, array2.length);
  const result = [];
  for (let i = 0; i < length; i++) {
    result.push([array1[i], array2[i]]);
  }
  return result;
}

const res = zip(["1","2","3"], [4,5,6])

This is the output of res:

[["1",4],["2",5],["3",6]]

So far, I was able to create the following snippet, but it's not ideal, since the result type does not know that the first item is a string and the second is a number, it says it can be either one of them:

function zip<T,U>(array1: T[], array2: U[]): (T|U)[][] {
  const length = Math.min(array1.length, array2.length);
  const result = [];
  for (let i = 0; i < length; i++) {
    result.push([array1[i], array2[i]]);
  }
  return result;
}

const res = zip(["1","2","3"], [4,5,6])
2 Answers

On top of answer by @apokryfos, you may also consider generator-based version:

function* zip<T,U>(array1: T[], array2: U[]): Generator<[T, U]> {
  const length = Math.min(array1.length, array2.length);
  for (let i = 0; i < length; i++) {
    yield [array1[i], array2[i]];
  }
}

const res = zip(["1","2","3"], [4,5,6])
for (const [s, n] of res) {
  console.log(`${s} ${n}`);
}

In TypeScript terms, zip basically returns an array of tuples which can be typed as:

function zip<T,U>(array1: T[], array2: U[]): [ T, U ][] {
  const length = Math.min(array1.length, array2.length);
  const result: [ T, U ][] = [];
  for (let i = 0; i < length; i++) {
    result.push([array1[i], array2[i]]);
  }
  return result;
}

const res = zip(["1","2","3"], [4,5,6])

This ensures each entry of the result is an array with T first and U second

Related