Repository: marpple/FxTS
Pull request #10 (2021-11-20 03:17:45)

feat: add overloading type of 'filter' function
This PR is to make better type inference of filter function when boolean constructor is used.

```ts
const res = filter(Boolean, [undefined, null, 0, "", false, -0, 123] as const) // IterableIterator<123> 
```


Patch:
diff --git a/README.md b/README.md
index 40b1575d..8ab767c3 100644
--- a/README.md
+++ b/README.md
@@ -46,10 +46,6 @@ you can also handle asynchronous data, see the [example](https://fxts.dev/docs/o
 
 - `npm test`
 
-## Running Type Test
-
-- `npm run compile:check`
-
 ## License
 
 Apache License 2.0
diff --git a/src/Lazy/filter.ts b/src/Lazy/filter.ts
index bd829ba2..477670c7 100644
--- a/src/Lazy/filter.ts
+++ b/src/Lazy/filter.ts
@@ -3,6 +3,7 @@ import concurrent, { Concurrent, isConcurrent } from "./concurrent";
 import pipe1 from "../pipe1";
 import IterableInfer from "../types/IterableInfer";
 import ReturnIterableIteratorType from "../types/ReturnIterableIteratorType";
+import TruthyTypesOf from "../types/TrutyTypesOf";
 import { Reject, Resolve } from "../types/Utils";
 import { AsyncFunctionException } from "../_internal/error";
 
@@ -212,16 +213,32 @@ function* sync<A>(f: (a: A) => unknown, iterable: Iterable<A>) {
  * see {@link https://fxts.dev/docs/pipe | pipe}, {@link https://fxts.dev/docs/toAsync | toAsync},
  * {@link https://fxts.dev/docs/toArray | toArray}
  */
+function filter<A>(
+  f: BooleanConstructor,
+  iterable: Iterable<A>,
+): IterableIterator<TruthyTypesOf<A>>;
+
 function filter<A, B = unknown>(
   f: (a: A) => B,
   iterable: Iterable<A>,
 ): IterableIterator<A>;
 
+function filter<A>(
+  f: BooleanConstructor,
+  iterable: AsyncIterable<A>,
+): AsyncIterableIterator<TruthyTypesOf<A>>;
+
 function filter<A, B = unknown>(
   f: (a: A) => B,
   iterable: AsyncIterable<A>,
 ): AsyncIterableIterator<A>;
 
+function filter<A extends Iterable<unknown> | AsyncIterable<unknown>>(
+  f: BooleanConstructor,
+): (
+  iterable: A,
+) => ReturnIterableIteratorType<A, TruthyTypesOf<IterableInfer<A>>>;
+
 function filter<
   A extends Iterable<unknown> | AsyncIterable<unknown>,
   B = unknown,
@@ -237,8 +254,13 @@ function filter<
   iterable?: A,
 ):
   | IterableIterator<IterableInfer<A>>
+  | IterableIterator<TruthyTypesOf<IterableInfer<A>>>
   | AsyncIterableIterator<IterableInfer<A>>
-  | ((iterable: A) => ReturnIterableIteratorType<A, IterableInfer<A>>) {
+  | AsyncIterableIterator<TruthyTypesOf<IterableInfer<A>>>
+  | ((iterable: A) => ReturnIterableIteratorType<A, IterableInfer<A>>)
+  | ((
+      iterable: A,
+    ) => ReturnIterableIteratorType<A, TruthyTypesOf<IterableInfer<A>>>) {
   if (iterable === undefined) {
     return (iterable: A): ReturnIterableIteratorType<A, IterableInfer<A>> =>
       filter(f, iterable as any) as ReturnIterableIteratorType<
diff --git a/src/types/Falsy.ts b/src/types/Falsy.ts
new file mode 100644
index 00000000..79a90ab4
--- /dev/null
+++ b/src/types/Falsy.ts
@@ -0,0 +1,3 @@
+type Falsy = null | undefined | false | 0 | -0 | 0n | "";
+
+export default Falsy;
diff --git a/src/types/TrutyTypesOf.ts b/src/types/TrutyTypesOf.ts
new file mode 100644
index 00000000..359a5242
--- /dev/null
+++ b/src/types/TrutyTypesOf.ts
@@ -0,0 +1,5 @@
+import Falsy from "./Falsy";
+
+type TruthyTypesOf<T> = T extends Falsy ? never : T;
+
+export default TruthyTypesOf;


Test patch:
diff --git a/test/Lazy/filter.spec.ts b/test/Lazy/filter.spec.ts
index b0cf14d8..642c4935 100644
--- a/test/Lazy/filter.spec.ts
+++ b/test/Lazy/filter.spec.ts
@@ -22,6 +22,13 @@ describe("filter", function () {
       expect(res).toEqual([2, 4, 6, 8]);
     });
 
+    it("should be filtered by the boolean constructor", function () {
+      const res = [
+        ...filter(Boolean, [undefined, null, 0, "", false, -0, 123]),
+      ];
+      expect(res).toEqual([123]);
+    });
+
     it("should throw an error when the callback is asynchronous", function () {
       const res = () => [...filter(modAsync, range(1, 10))];
       expect(res).toThrowError(new AsyncFunctionException());
@@ -58,6 +65,18 @@ describe("filter", function () {
       expect(res).toEqual([2, 4, 6, 8]);
     });
 
+    it("should be filtered by the boolean constructor", async function () {
+      const res: unknown[] = [];
+      const iter = filter(
+        Boolean,
+        toAsync([undefined, null, 0, "", false, -0, 123]),
+      );
+      for await (const item of iter) {
+        res.push(item);
+      }
+      expect(res).toEqual([123]);
+    });
+
     it("should be able to handle an error", async function () {
       try {
         await toArray(
@@ -252,6 +271,7 @@ describe("filter", function () {
         ),
       ).rejects.toThrow("err");
     });
+
     it("should be able to be used as a curried function in the pipeline", async function () {
       const res = await pipe(
         toAsync([1, 2, 3, 4]),
diff --git a/type-check/Lazy/filter.test.ts b/type-check/Lazy/filter.test.ts
index 1eede254..6a0532f9 100644
--- a/type-check/Lazy/filter.test.ts
+++ b/type-check/Lazy/filter.test.ts
@@ -6,35 +6,52 @@ const { checks, check } = Test;
 const res1 = filter((a) => a % 2 === 0, []);
 const res2 = filter((a) => a % 2 === 0, [1, 2, 3]);
 const res3 = filter(async (a) => a % 2 === 0, [1, 2, 3]);
-const res4 = filter((a) => a % 2 === 0, toAsync([1, 2, 3]));
-const res5 = filter(async (a) => a % 2 === 0, toAsync([1, 2, 3]));
+const res4 = filter(Boolean, [undefined, null, 0, "", false, -0, 123] as const);
+const res5 = filter((a) => a % 2 === 0, toAsync([1, 2, 3]));
+const res6 = filter(async (a) => a % 2 === 0, toAsync([1, 2, 3]));
+const res7 = filter(
+  Boolean,
+  toAsync([undefined, null, 0, "", false, -0, 123] as const),
+);
 
-const res6 = pipe(
+const res8 = pipe(
   [1, 2, 3, 4],
   filter((a) => a % 2 === 0),
 );
-const res7 = pipe(
+const res9 = pipe(
   [1, 2, 3, 4],
   filter(async (a) => a % 2 === 0),
 );
-const res8 = pipe(
+const res10 = pipe(
+  [undefined, null, 0, "", false, -0, 123] as const,
+  filter(Boolean),
+);
+const res11 = pipe(
   toAsync([1, 2, 3, 4]),
   filter((a) => a % 2 === 0),
 );
-const res9 = pipe(
+const res12 = pipe(
   toAsync([1, 2, 3, 4]),
   filter(async (a) => a % 2 === 0),
 );
+const res13 = pipe(
+  toAsync([undefined, null, 0, "", false, -0, 123] as const),
+  filter(Boolean),
+);
 
 checks([
   check<typeof res1, IterableIterator<never>, Test.Pass>(),
   check<typeof res2, IterableIterator<number>, Test.Pass>(),
   check<typeof res3, IterableIterator<number>, Test.Pass>(),
-  check<typeof res4, AsyncIterableIterator<number>, Test.Pass>(),
+  check<typeof res4, IterableIterator<123>, Test.Pass>(),
   check<typeof res5, AsyncIterableIterator<number>, Test.Pass>(),
+  check<typeof res6, AsyncIterableIterator<number>, Test.Pass>(),
+  check<typeof res7, AsyncIterableIterator<123>, Test.Pass>(),
 
-  check<typeof res6, IterableIterator<number>, Test.Pass>(),
-  check<typeof res7, IterableIterator<number>, Test.Pass>(),
-  check<typeof res8, AsyncIterableIterator<number>, Test.Pass>(),
-  check<typeof res9, AsyncIterableIterator<number>, Test.Pass>(),
+  check<typeof res8, IterableIterator<number>, Test.Pass>(),
+  check<typeof res9, IterableIterator<number>, Test.Pass>(),
+  check<typeof res10, IterableIterator<123>, Test.Pass>(),
+  check<typeof res11, AsyncIterableIterator<number>, Test.Pass>(),
+  check<typeof res12, AsyncIterableIterator<number>, Test.Pass>(),
+  check<typeof res13, AsyncIterableIterator<123>, Test.Pass>(),
 ]);