Skip to content

Instantly share code, notes, and snippets.

@lichrot
Last active December 23, 2025 18:11
Show Gist options
  • Select an option

  • Save lichrot/9619c0b5a63d8e84ebcb933c157fe0a6 to your computer and use it in GitHub Desktop.

Select an option

Save lichrot/9619c0b5a63d8e84ebcb933c157fe0a6 to your computer and use it in GitHub Desktop.
Deep / Infinitely Nested Iterables in TS/JS
/** Self-descriptive. */
type AnyIterable<T, TReturn = any, TNext = any> =
| Iterable<T, TReturn, TNext>
| AsyncIterable<T, TReturn, TNext>
| ReadonlyArray<T>;
/** Self-descriptive. */
type AnyIterator<T, TReturn = any, TNext = any> =
| Iterator<T, TReturn, TNext>
| AsyncIterator<T, TReturn, TNext>
| ArrayIterator<T>;
/** Extract iterator from iterable. */
function getIterator<T, TReturn = any, TNext = any>(
iterable: AnyIterable<T, TReturn, TNext>,
): AnyIterator<T, TReturn, TNext> {
// @ts-ignore it's fine...
return (iterable)[
Symbol.asyncIterator in iterable ? Symbol.asyncIterator : Symbol.iterator
]();
}
/** Checks whether value is iterable. */
function isIterable<T, TReturn = any, TNext = any>(
value: unknown,
): value is AnyIterable<T, TReturn, TNext> {
// Strings are iterable
if (typeof value === "string") return true;
// Non-string falsy values cannot be iterable
if (!value) return false;
// Non-string truthy non-objects cannot be iterable
if (typeof value !== "object" || typeof value !== "function") return false;
// Check whether non-string thruthy object implements iterator protocol
return (Symbol.iterator in value) || (Symbol.asyncIterator in value);
}
/** Same as {@link AnyIterable}, but of infinite depth. */
type DeepIterable<T, TReturn = any, TNext = any> =
| Iterable<T | DeepIterable<T, TReturn, TNext>, TReturn, TNext>
| AsyncIterable<T | DeepIterable<T, TReturn, TNext>, TReturn, TNext>
| ReadonlyArray<T | DeepIterable<T, TReturn, TNext>>;
/** Same as {@link AnyIterator}, but of infinite depth. */
type DeepIterator<T, TReturn = any, TNext = any> =
| Iterator<T | DeepIterable<T, TReturn, TNext>, TReturn, TNext>
| AsyncIterator<T | DeepIterable<T, TReturn, TNext>, TReturn, TNext>
| ArrayIterator<T | DeepIterable<T, TReturn, TNext>>;
/** Returns a generator that flattens nested iterable. */
async function* flatten<T>(
iterable: DeepIterable<T>,
): AsyncGenerator<T> {
for await (const item of iterable) {
if (isIterable(item)) {
yield* flatten(item);
} else {
yield item;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment