Last active
December 23, 2025 18:11
-
-
Save lichrot/9619c0b5a63d8e84ebcb933c157fe0a6 to your computer and use it in GitHub Desktop.
Deep / Infinitely Nested Iterables in TS/JS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** 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