Skip to content

Commit 4a4d1bb

Browse files
authored
Merge pull request #2 from jsr-core/allow-malformed-iterator-on-chain
feat: support chaining malformed iterables
2 parents 2f5ea63 + 15d7c3e commit 4a4d1bb

File tree

4 files changed

+72
-11
lines changed

4 files changed

+72
-11
lines changed

async/chain.ts

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,33 @@
1212
* const iter = chain([1, 2], [3, 4]);
1313
* console.log(await toArray(iter)); // [1, 2, 3, 4]
1414
* ```
15+
*
16+
* It supports chaining malformed iterables.
17+
*
18+
* @example
19+
* ```ts
20+
* import { toArray } from "@core/iterutil/async/to-array";
21+
* import { chain } from "@core/iterutil/async/chain";
22+
*
23+
* const iter = chain([1, 2], ["a", "b"], [true]);
24+
* console.log(await toArray(iter)); // [1, 2, "a", "b", true]
25+
* ```
1526
*/
16-
export async function* chain<T>(
17-
...iterables: (Iterable<T> | AsyncIterable<T>)[]
18-
): AsyncIterable<T> {
27+
export async function* chain<
28+
T extends (Iterable<unknown> | AsyncIterable<unknown>)[],
29+
>(
30+
...iterables: T
31+
): AsyncIterable<Chain<T>> {
1932
for await (const iterable of iterables) {
2033
for await (const value of iterable) {
21-
yield value;
34+
yield value as Chain<T>;
2235
}
2336
}
2437
}
38+
39+
export type Chain<T> = T extends readonly [] ? never
40+
: T extends readonly [Iterable<infer U>] ? U
41+
: T extends readonly [AsyncIterable<infer U>] ? U
42+
: T extends readonly [Iterable<infer U>, ...infer R] ? U | Chain<R>
43+
: T extends readonly [AsyncIterable<infer U>, ...infer R] ? U | Chain<R>
44+
: never;

async/chain_test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,17 @@ Deno.test("chain", async (t) => {
3333
assertEquals(await toArray(result), expected);
3434
assertType<IsExact<typeof result, AsyncIterable<number>>>(true);
3535
});
36+
37+
await t.step("with malform iterable", async () => {
38+
const result = chain(
39+
toAsyncIterable([1, 2]),
40+
["a", "b"],
41+
toAsyncIterable([true]),
42+
);
43+
const expected = [1, 2, "a", "b", true];
44+
assertEquals(await toArray(result), expected);
45+
assertType<
46+
IsExact<typeof result, AsyncIterable<number | string | boolean>>
47+
>(true);
48+
});
3649
});

chain.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,26 @@
1111
* const iter = chain([1, 2], [3, 4]);
1212
* console.log([...iter]); // [1, 2, 3, 4]
1313
* ```
14+
*
15+
* It supports chaining malformed iterables.
16+
*
17+
* @example
18+
* ```ts
19+
* import { chain } from "@core/iterutil/chain";
20+
*
21+
* const iter = chain([1, 2], ["a", "b"], [true]);
22+
* console.log([...iter]); // [1, 2, "a", "b", true]
23+
* ```
1424
*/
15-
export function* chain<T>(...iterables: Iterable<T>[]): Iterable<T> {
25+
export function* chain<T extends Iterable<unknown>[]>(
26+
...iterables: T
27+
): Iterable<Chain<T>> {
1628
for (const iterable of iterables) {
17-
yield* iterable;
29+
yield* iterable as Iterable<Chain<T>>;
1830
}
1931
}
32+
33+
export type Chain<T> = T extends readonly [] ? never
34+
: T extends readonly [Iterable<infer U>] ? U
35+
: T extends readonly [Iterable<infer U>, ...infer R] ? U | Chain<R>
36+
: never;

chain_test.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,20 @@ import { assertEquals } from "@std/assert";
22
import { assertType, type IsExact } from "@std/testing/types";
33
import { chain } from "./chain.ts";
44

5-
Deno.test("chain", () => {
6-
const result = chain([1, 2], [3, 4], [5]);
7-
const expected = [1, 2, 3, 4, 5];
8-
assertEquals([...result], expected);
9-
assertType<IsExact<typeof result, Iterable<number>>>(true);
5+
Deno.test("chain", async (t) => {
6+
await t.step("uniform iterables", () => {
7+
const result = chain([1, 2], [3, 4], [5]);
8+
const expected = [1, 2, 3, 4, 5];
9+
assertEquals([...result], expected);
10+
assertType<IsExact<typeof result, Iterable<number>>>(true);
11+
});
12+
13+
await t.step("malform iterables", () => {
14+
const result = chain([1, 2], ["a", "b"], [true]);
15+
const expected = [1, 2, "a", "b", true];
16+
assertEquals([...result], expected);
17+
assertType<IsExact<typeof result, Iterable<number | string | boolean>>>(
18+
true,
19+
);
20+
});
1021
});

0 commit comments

Comments
 (0)