Skip to content

Commit 730f9f5

Browse files
authored
feat: use better typing for .find() (#1)
* feat: use better typing for .find() * test: improve test coverage * ci: add explicit run for v1.16 * test: refactor
1 parent 5ef5a71 commit 730f9f5

File tree

5 files changed

+96
-81
lines changed

5 files changed

+96
-81
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ jobs:
4040
deno: v1.10
4141
- os: ubuntu-latest
4242
deno: v1.13
43+
- os: ubuntu-latest
44+
deno: v1.16
4345
- os: ubuntu-latest
4446
deno: canary
4547
- os: windows-latest

lib/wrap_async_iterator.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,14 @@ export type WrappedAsyncIterator<T> = {
3636
forEach: (fn: (t: T) => void | Promise<void>) => Promise<void>;
3737
some: (fn: (t: T) => boolean | Promise<boolean>) => Promise<boolean>;
3838
every: (fn: (t: T) => boolean | Promise<boolean>) => Promise<boolean>;
39-
find: (fn: (t: T) => boolean | Promise<boolean>) => Promise<T | undefined>;
39+
find: {
40+
<U extends T>(
41+
filtererFn: (t: T) => t is U,
42+
): Promise<U | undefined>;
43+
(
44+
filtererFn: (t: T) => boolean | Promise<boolean>,
45+
): Promise<T | undefined>;
46+
};
4047
};
4148

4249
export const wrapAsyncIterator = <T>(
@@ -186,7 +193,7 @@ export const wrapAsyncIterator = <T>(
186193
}
187194
return true;
188195
},
189-
find: async (fn) => {
196+
find: async (fn: any) => {
190197
if (typeof fn !== "function") {
191198
throw new TypeError(`${fn} is not a function`);
192199
}

lib/wrap_iterator.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,14 @@ export type WrappedIterator<T> = {
3030
forEach: (fn: (t: T) => void) => void;
3131
some: (fn: (t: T) => boolean) => boolean;
3232
every: (fn: (t: T) => boolean) => boolean;
33-
find: (fn: (t: T) => boolean) => T | undefined;
33+
find: {
34+
<U extends T>(
35+
filtererFn: (t: T) => t is U,
36+
): U | undefined;
37+
(
38+
filtererFn: (t: T) => boolean,
39+
): T | undefined;
40+
};
3441
};
3542

3643
export const wrapIterator = <T>(ite: Iterator<T>): WrappedIterator<T> => {
@@ -169,7 +176,7 @@ export const wrapIterator = <T>(ite: Iterator<T>): WrappedIterator<T> => {
169176
}
170177
return true;
171178
},
172-
find: (fn) => {
179+
find: (fn: any) => {
173180
if (typeof fn !== "function") {
174181
throw new TypeError(`${fn} is not a function`);
175182
}

test/unit/wrap_async_iterator.spec.ts

Lines changed: 39 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,22 @@ import { asserts } from "../../deps.ts";
44
import { wrapAsyncIterator } from "../../lib/wrap_async_iterator.ts";
55
import { asyncIteratorFrom } from "../../lib/async_iterator_from.ts";
66

7+
const naturals = async function* naturals() {
8+
let i = 0;
9+
while (true) {
10+
await Promise.resolve();
11+
yield i;
12+
i += 1;
13+
}
14+
};
15+
16+
type Prime10 = 2 | 3 | 5 | 7;
17+
const isPrime10 = (n: number): n is Prime10 =>
18+
n === 2 || n === 3 || n === 5 || n === 7;
19+
720
Deno.test({
821
name: "AsyncIterator.prototype.map",
922
async fn() {
10-
const naturals = async function* naturals() {
11-
let i = 0;
12-
while (true) {
13-
await Promise.resolve();
14-
yield i;
15-
i += 1;
16-
}
17-
};
1823
const naturalsStr: AsyncIterator<string> = wrapAsyncIterator(naturals())
1924
.map((e) => e.toString())
2025
.unwrap();
@@ -32,16 +37,7 @@ Deno.test({
3237
Deno.test({
3338
name: "AsyncIterator.prototype.filter",
3439
async fn() {
35-
const naturals = async function* naturals() {
36-
let i = 0;
37-
while (true) {
38-
yield i;
39-
i += 1;
40-
}
41-
};
42-
const isPrime10 = (n: number): n is 2 | 3 | 5 | 7 =>
43-
n === 2 || n === 3 || n === 5 || n === 7;
44-
const prime10: AsyncIterator<2 | 3 | 5 | 7> = wrapAsyncIterator(naturals())
40+
const prime10: AsyncIterator<Prime10> = wrapAsyncIterator(naturals())
4541
.filter(isPrime10)
4642
.unwrap();
4743
asserts.assertEquals(
@@ -58,13 +54,6 @@ Deno.test({
5854
Deno.test({
5955
name: "AsyncIterator.prototype.take",
6056
async fn() {
61-
const naturals = async function* naturals() {
62-
let i = 0;
63-
while (true) {
64-
yield i;
65-
i += 1;
66-
}
67-
};
6857
asserts.assertEquals(
6958
await wrapAsyncIterator(naturals()).take(4).toArray(),
7059
[0, 1, 2, 3],
@@ -117,13 +106,6 @@ Deno.test({
117106
name: "AsyncIterator.prototype.drop",
118107
async fn() {
119108
{
120-
const naturals = async function* naturals() {
121-
let i = 0;
122-
while (true) {
123-
yield i;
124-
i += 1;
125-
}
126-
};
127109
asserts.assertEquals(
128110
await wrapAsyncIterator(naturals()).drop(4).take(4).toArray(),
129111
[4, 5, 6, 7],
@@ -192,13 +174,6 @@ Deno.test({
192174
Deno.test({
193175
name: "AsyncIterator.prototype.asIndexedPairs",
194176
async fn() {
195-
const naturals = async function* naturals() {
196-
let i = 0;
197-
while (true) {
198-
yield i;
199-
i += 1;
200-
}
201-
};
202177
asserts.assertEquals(
203178
await wrapAsyncIterator(naturals())
204179
.filter((e) => e % 2 === 1)
@@ -225,6 +200,31 @@ Deno.test({
225200
.toArray(),
226201
[100, 200, 3, 4, 5, [6, 7], 8, 9, 10, 11],
227202
);
203+
asserts.assertEquals(
204+
await wrapAsyncIterator(asyncIteratorFrom([
205+
1,
206+
]))
207+
.flatMap<number | number[]>(async function* (e) {
208+
yield e * 10;
209+
yield e * 20;
210+
})
211+
.toArray(),
212+
[10, 20],
213+
);
214+
await asserts.assertRejects(
215+
async () =>
216+
await wrapAsyncIterator(asyncIteratorFrom([0])).flatMap(() => ({
217+
[Symbol.asyncIterator]: 0,
218+
})).toArray(),
219+
TypeError,
220+
);
221+
await asserts.assertRejects(
222+
async () =>
223+
await wrapAsyncIterator(asyncIteratorFrom([0])).flatMap(() => ({
224+
[Symbol.iterator]: 0,
225+
})).toArray(),
226+
TypeError,
227+
);
228228
asserts.assertThrows(
229229
() => wrapAsyncIterator(asyncIteratorFrom([1, 2, 3])).flatMap(1 as any),
230230
TypeError,

test/unit/wrap_iterator.spec.ts

Lines changed: 37 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,21 @@ import { asserts } from "../../deps.ts";
44
import { iteratorFrom } from "../../lib/iterator_from.ts";
55
import { wrapIterator } from "../../lib/wrap_iterator.ts";
66

7+
const naturals = function* naturals() {
8+
let i = 0;
9+
while (true) {
10+
yield i;
11+
i += 1;
12+
}
13+
};
14+
15+
type Prime10 = 2 | 3 | 5 | 7;
16+
const isPrime10 = (n: number): n is Prime10 =>
17+
n === 2 || n === 3 || n === 5 || n === 7;
18+
719
Deno.test({
820
name: "Iterator.prototype.map",
921
fn() {
10-
const naturals = function* naturals() {
11-
let i = 0;
12-
while (true) {
13-
yield i;
14-
i += 1;
15-
}
16-
};
1722
const naturalsStr: Iterator<string> = wrapIterator(naturals())
1823
.map((e) => e.toString())
1924
.unwrap();
@@ -31,16 +36,7 @@ Deno.test({
3136
Deno.test({
3237
name: "Iterator.prototype.filter",
3338
fn() {
34-
const naturals = function* naturals() {
35-
let i = 0;
36-
while (true) {
37-
yield i;
38-
i += 1;
39-
}
40-
};
41-
const isPrime10 = (n: number): n is 2 | 3 | 5 | 7 =>
42-
n === 2 || n === 3 || n === 5 || n === 7;
43-
const prime10: Iterator<2 | 3 | 5 | 7> = wrapIterator(naturals())
39+
const prime10: Iterator<Prime10> = wrapIterator(naturals())
4440
.filter(isPrime10)
4541
.unwrap();
4642
asserts.assertEquals(
@@ -57,13 +53,6 @@ Deno.test({
5753
Deno.test({
5854
name: "Iterator.prototype.take",
5955
fn() {
60-
const naturals = function* naturals() {
61-
let i = 0;
62-
while (true) {
63-
yield i;
64-
i += 1;
65-
}
66-
};
6756
asserts.assertEquals(
6857
wrapIterator(naturals()).take(4).toArray(),
6958
[0, 1, 2, 3],
@@ -115,13 +104,6 @@ Deno.test({
115104
name: "Iterator.prototype.drop",
116105
fn() {
117106
{
118-
const naturals = function* naturals() {
119-
let i = 0;
120-
while (true) {
121-
yield i;
122-
i += 1;
123-
}
124-
};
125107
asserts.assertEquals(
126108
wrapIterator(naturals()).drop(4).take(4).toArray(),
127109
[4, 5, 6, 7],
@@ -190,13 +172,6 @@ Deno.test({
190172
Deno.test({
191173
name: "Iterator.prototype.asIndexedPairs",
192174
fn() {
193-
const naturals = function* naturals() {
194-
let i = 0;
195-
while (true) {
196-
yield i;
197-
i += 1;
198-
}
199-
};
200175
asserts.assertEquals(
201176
wrapIterator(naturals())
202177
.filter((e) => e % 2 === 1)
@@ -223,6 +198,24 @@ Deno.test({
223198
.toArray(),
224199
[100, 200, 3, 4, 5, [6, 7], 8, 9, 10, 11],
225200
);
201+
asserts.assertEquals(
202+
wrapIterator(iteratorFrom([
203+
1,
204+
]))
205+
.flatMap<number | number[]>(function* (e) {
206+
yield e * 10;
207+
yield e * 20;
208+
})
209+
.toArray(),
210+
[10, 20],
211+
);
212+
asserts.assertThrows(
213+
() =>
214+
wrapIterator(iteratorFrom([0])).flatMap(() => ({
215+
[Symbol.iterator]: 0,
216+
})).toArray(),
217+
TypeError,
218+
);
226219
asserts.assertThrows(
227220
() => wrapIterator(iteratorFrom([1, 2, 3])).flatMap(1 as any),
228221
TypeError,
@@ -386,6 +379,12 @@ Deno.test({
386379
TypeError,
387380
);
388381
}
382+
{
383+
const got: Prime10 | undefined = wrapIterator(naturals())
384+
.take(10)
385+
.find(isPrime10);
386+
asserts.assertEquals(got, 2);
387+
}
389388
{
390389
asserts.assertEquals(
391390
wrapIterator(

0 commit comments

Comments
 (0)