DEV Community

loading...

Zip Iterator in Typescript

chrismilson profile image Chris Milson ・2 min read

The code in this article and an implementation of zipLongest are in this gist.

The built-in zip function is ubiquitous in python. It allows highly readable iteration over multiple iterables:

def naturals():
    n = 1
    while True:
        yield n
        n += 1

for n, c in zip(naturals(), "Hello!"):
    print(n, c)
# 1 H
# 2 e
# 3 l
# 4 l
# 5 o
# 6 !
Enter fullscreen mode Exit fullscreen mode

Now, with Mapped Types in typescript, we can do the same thing, all while keeping our lovely types too!

With mapped types, we can make an Iterableify type, which will take a type, and change it so that its members are of type Iterable:

type Iterableify<T> = { [K in keyof T]: Iterable<T[K]> }
Enter fullscreen mode Exit fullscreen mode

For example

type T1 = Iterableify<string[]> // Iterable<string>[]
type T2 = Iterableify<{a: number, b: string}> // {a: Iterable<number>, b: Iterable<string>}
type T3 = Iterableify<[number, string][]> // Iterable<[number, string]>[]
Enter fullscreen mode Exit fullscreen mode

We can then make a simple zip function like so:

function* zip<T extends Array<any>>(
    ...toZip: Iterableify<T>
): Generator<T> {
    // Get iterators for all of the iterables.
    const iterators = toZip.map(i => i[Symbol.iterator]())

    while (true) {
        // Advance all of the iterators.
        const results = iterators.map(i => i.next())

        // If any of the iterators are done, we should stop.
        if (results.some(({ done }) => done)) {
            break
        }

        // We can assert the yield type, since we know none
        // of the iterators are done.
        yield results.map(({ value }) => value) as T
    }
}
Enter fullscreen mode Exit fullscreen mode

Since the mapped types affect typescript tuples as well, we can now do something like this:

for (const [a, b] of zip([1, 2, 3], "abc")) {
    // type of a is number!
    // type of b is string!

    // This has no compilation errors!
    console.log(b.repeat(a))
}
Enter fullscreen mode Exit fullscreen mode

Discussion (1)

pic
Editor guide
Collapse
alesgenova profile image
Alessandro Genova

Nice, zip in python is really convenient, thank you for this type safe implementation in typescript!