Home > OS >  Typescript writing zip function type declaration for any number of parameters
Typescript writing zip function type declaration for any number of parameters

Time:01-11

I wrote an iterator class and a method for zipping two iterators which takes 1 argument and its type declaration looks like this:

zip<B>(other: Iterable<B> | Iterator<B>): ItIterator<[T, B]>

where T is the type of this.next().value.

However I can't grasp how to write it so it takes any number of arguments and returns an iterator over a tuple such that

ItIterator.prototype.zip.call([1][Symbol.iterator](), ['a'], [false])

would return ItIterator<[number, string, boolean]>

Is there a way to do this?

CodePudding user response:

Here's the approach I'd take:

declare class ItIterator<T> {
  zip<B extends any[]>(
    ...other: { [I in keyof B]: Iterable<B[I]> | Iterator<B[I]> }
  ): ItIterator<[T, ...B]>;
}

The idea is that zip() is generic in B, the tuple type of the element types of the other iterables. I mean that if you call zip(x, y, z) where x is an Iterable<X>, y is an Iterable<Y>, and z is an Iterable<Z>, then the type argument B will be [X, Y, Z].

This is accomplished by having the rest parameter tuple type of other be a mapped tuple type over B.

Then the output type is an ItIterator<> of the variadic tuple type [T, ...B], where we prepend T to the tuple of B.


Let's test it out:

declare const i: ItIterator<string>;

const y = i.zip([1], [true], [new Date(), new Date()]);
// const y: ItIterator<[string, number, boolean, Date]>

Looks good. Note that I wouldn't try to support

const z = ItIterator.prototype.zip.call([1][Symbol.iterator](), ['a'], [false]);
// const z: ItIterator<[any, ...any[]]>

because the typing support for the call() method of functions does not work well with functions that are themselves generic, and you end up getting just the constraint of ItIterator<[any, ...any[]]>.

Playground link to code

  • Related