Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Match array return types with arbitrary number of inputs using generics

Tags:

typescript

If a user passes in type [Iterable<T>] I want to return type [T] and if the user passes in [Iterable<T>, Iterable<U>] I want to return type [T, U]. I want to continue this for any number of elements in the input array.

Is there a way to define the types for the following function generically so that it will work for any number of inputs rather than continuing to define overloads of the function with increasing numbers of generic?

Here's what I have so far, but of course it only supports up to 5 inputs as defined. Can I make it work for an arbitrary number of inputs?

export function linkIterables<T>(
  iterables: [Iterable<T>],
): Iterable<[T]>;
export function linkIterables<T, U>(
  iterables: [Iterable<T>, Iterable<U>],
): Iterable<[T, U]>;
export function linkIterables<T, U, V>(
  iterables: [Iterable<T>, Iterable<U>, Iterable<V>],
): Iterable<[T, U, V]>;
export function linkIterables<T, U, V, W>(
  iterables: [Iterable<T>, Iterable<U>, Iterable<V>, Iterable<W>],
): Iterable<[T, U, V, W]>;
export function linkIterables<T, U, V, W, X>(
  iterables: [Iterable<T>, Iterable<U>, Iterable<V>, Iterable<W>, Iterable<X>],
): Iterable<[T, U, V, W, X]>;
export function* linkIterables<T>(
  iterables: Iterable<Iterable<T>>,
): Iterable<T[]> {
  const iters = [...iterables].map(u => u[Symbol.iterator]());
  while (true) {
    let done = true;
    const results = [];
    for (const iter of iters) {
      const result = iter.next();
      if (!result.done) {
        done = false;
      }
      results.push(result.value);
    }
    if (done) {
      return results;
    }
    yield results;
  }
};
like image 759
Nathan Wall Avatar asked Feb 11 '26 00:02

Nathan Wall


1 Answers

Yes, you can use mapped types on array and tuple types to represent the general operation of turning an output tuple type T into an input tuple type where at each numeric index I in T will have a property of type Iterable<T[I]>. Like this:

export function linkIterables<T extends any[]>(
  iterables: [...{ [I in keyof T]: Iterable<T[I]> }]
): Iterable<T>;

Note that wrapping the type of iterables in [... ] uses variadic tuple type syntax to hint that we want the compiler to interpret iterables as a tuple type if possible and not just an array. Let's try it:

const result = linkIterables([
  [1, 2],
  ["a", "b"],
  [true, false],
  [new Date(), new Date()],
  [document, document],
  [null, null]
]);

// const result: Iterable<[number, string, boolean, Date, Document, null]>

Looks good!

Playground link to code

like image 86
jcalz Avatar answered Feb 18 '26 14:02

jcalz



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!