Repository: marpple/FxTS (ts)

Issue:
`partition` function to be able to use Generic parameter
# Suggestion

## ⭐ Suggestion

<!-- What would you like to be added? -->

## 💻 Use Cases

```ts
type ObjA = { _type: "A"; a: string; b: string };
type ObjB = { _type: "B"; a: number; b: number };

const list = [
  { _type: "A", a: "1", b: "2" },
  { _type: "B", a: 3, b: 4 },
  { _type: "A", a: "1", b: "2" },
  { _type: "B", a: 3, b: 4 }
];

const [listA, lisetB] = toArray(partition((a): a is ObjA => a._type === "B", list));

// listA: ObjA[], listB: ObjB[]
```


Pull request description:
feat: improved type inference of `partition` function
Fixes #181 

Improved type inference of `partition` function when receiving custom typeguard function as an argument.
```ts
const res = partition(
  (a): a is number => typeof a === "number",
  [1, 2, "a", "b", true, false],
);
// As-is
// typeof res == [(number | string | boolean)[], (number | string | boolean)[]]

// To-be
// typeof res == [number[], (string | boolean)[]]
```

Patch:
diff --git a/.commitlintrc b/.commitlintrc
index c86a9a30..2b7b4371 100644
--- a/.commitlintrc
+++ b/.commitlintrc
@@ -1,3 +1,6 @@
 {
-  extends: ["@commitlint/config-conventional"]
+  extends: ["@commitlint/config-conventional"],
+  rules: {
+    "header-max-length": [1, 'always', 110]
+  }
 }
diff --git a/src/Lazy/reject.ts b/src/Lazy/reject.ts
index f7a7da0a..89f291c5 100644
--- a/src/Lazy/reject.ts
+++ b/src/Lazy/reject.ts
@@ -53,6 +53,28 @@ import ReturnIterableIteratorType from "../types/ReturnIterableIteratorType";
  * see {@link https://fxts.dev/docs/pipe | pipe}, {@link https://fxts.dev/docs/toAsync | toAsync},
  * {@link https://fxts.dev/docs/toArray | toArray}
  */
+
+function reject<A, B extends A>(
+  f: (a: A) => a is B,
+  iterable: Iterable<A>,
+): IterableIterator<Exclude<A, B>>;
+
+function reject<A, B extends A>(
+  f: (a: A) => a is B,
+  iterable: AsyncIterable<A>,
+): AsyncIterableIterator<Exclude<A, B>>;
+
+function reject<
+  A extends Iterable<unknown> | AsyncIterable<unknown>,
+  B extends IterableInfer<A>,
+>(
+  f: (a: IterableInfer<A>) => a is B,
+): (
+  iterable: A,
+) => A extends AsyncIterable<any>
+  ? AsyncIterableIterator<Exclude<IterableInfer<A>, B>>
+  : IterableIterator<Exclude<IterableInfer<A>, B>>;
+
 function reject<A, B = unknown>(
   f: (a: A) => B,
   iterable: Iterable<A>,
diff --git a/src/partition.ts b/src/partition.ts
index 3236ea83..2fe0db44 100644
--- a/src/partition.ts
+++ b/src/partition.ts
@@ -42,6 +42,27 @@ import { AsyncFunctionException } from "./_internal/error";
  *
  *  see {@link https://fxts.dev/docs/pipe | pipe}, {@link https://fxts.dev/docs/toAsync | toAsync}
  */
+
+function partition<A, L extends A, R extends A = Exclude<A, L>>(
+  f: (a: A) => a is L,
+  iterable: Iterable<A>,
+): [L[], R[]];
+
+function partition<A, L extends A, R extends A = Exclude<A, L>>(
+  f: (a: A) => a is L,
+  iterable: AsyncIterable<A>,
+): Promise<[L[], R[]]>;
+
+function partition<
+  A extends Iterable<unknown> | AsyncIterable<unknown>,
+  L extends IterableInfer<A>,
+  R = Exclude<IterableInfer<A>, L>,
+>(
+  f: (a: IterableInfer<A>) => a is L,
+): (
+  iterable: A,
+) => A extends AsyncIterable<any> ? Promise<[L[], R[]]> : [L[], R[]];
+
 function partition<A, B>(f: (a: A) => B, iterable: Iterable<A>): [A[], A[]];
 
 function partition<A, B>(


Test patch:
diff --git a/type-check/Lazy/reject.test.ts b/type-check/Lazy/reject.test.ts
index 56a1546a..b5b92548 100644
--- a/type-check/Lazy/reject.test.ts
+++ b/type-check/Lazy/reject.test.ts
@@ -1,5 +1,5 @@
 import * as Test from "../../src/types/Test";
-import { toAsync, reject, pipe } from "../../src";
+import { isNil, isString, pipe, reject, toAsync } from "../../src";
 
 const { checks, check } = Test;
 
@@ -25,6 +25,20 @@ const res8 = pipe(
   reject(async (a) => a % 2 === 0),
 );
 
+const res9 = reject(isNil, [1, 2, 3, "4", "5", "6", null, undefined]);
+const res10 = reject(isNil, toAsync([1, 2, 3, "4", "5", "6", null, undefined]));
+const res11 = pipe(
+  [1, 2, 3, "4", "5", "6", null, undefined],
+  reject(isNil),
+  reject(isString),
+);
+const res12 = pipe(
+  [1, 2, 3, "4", "5", "6", null, undefined],
+  toAsync,
+  reject(isNil),
+  reject(isString),
+);
+
 checks([
   check<typeof res1, IterableIterator<number>, Test.Pass>(),
   check<typeof res2, IterableIterator<number>, Test.Pass>(),
@@ -35,4 +49,9 @@ checks([
   check<typeof res6, IterableIterator<number>, Test.Pass>(),
   check<typeof res7, AsyncIterableIterator<number>, Test.Pass>(),
   check<typeof res8, AsyncIterableIterator<number>, Test.Pass>(),
+
+  check<typeof res9, IterableIterator<number | string>, Test.Pass>(),
+  check<typeof res10, AsyncIterableIterator<number | string>, Test.Pass>(),
+  check<typeof res11, IterableIterator<number>, Test.Pass>(),
+  check<typeof res12, AsyncIterableIterator<number>, Test.Pass>(),
 ]);
diff --git a/type-check/partition.test.ts b/type-check/partition.test.ts
index a4141fcd..5acc26f4 100644
--- a/type-check/partition.test.ts
+++ b/type-check/partition.test.ts
@@ -3,32 +3,53 @@ import { partition, pipe, toAsync } from "../src";
 
 const { checks, check } = Test;
 
-type Res = [(string | number)[], (string | number)[]];
+const res0 = partition((a) => typeof a === "string", []);
+const res1 = partition((a) => a > 3, [0, 1, 2, 3, 4]);
 
-const res1 = partition((a) => typeof a === "string", []);
-const res2: Res = partition(
-  (a) => typeof a === "number",
-  ["3", "4", "5", 1, 2, 3],
-);
-const res3: Res = pipe(
-  ["3", "4", "5", 1, 2, 3],
+type Res1 = [(string | number)[], (string | number)[]];
+const list1 = ["3", "4", "5", 1, 2, 3];
+const res2 = partition((a) => typeof a === "number", list1);
+const res3: Res1 = pipe(
+  list1,
   partition((a) => typeof a === "number"),
 );
 // throw AsyncFunctionException
-const res4: Res = pipe(
-  ["3", "4", "5", 1, 2, 3],
+const res4: Res1 = pipe(
+  list1,
   partition((a) => Promise.resolve(typeof a === "number")),
 );
-const res5: Promise<Res> = pipe(
-  ["3", "4", "5", 1, 2, 3],
+const res5: Promise<Res1> = pipe(
+  list1,
   toAsync,
   partition((a) => Promise.resolve(typeof a === "number")),
 );
 
+type Res2 = [number[], (string | boolean)[]];
+const list2 = ["3", "4", "5", 1, 2, 3, true, false];
+const res6 = partition((a): a is number => typeof a === "number", list2);
+const res7 = pipe(
+  list2,
+  partition((a): a is number => typeof a === "number"),
+);
+const res8 = partition(
+  (a): a is number => typeof a === "number",
+  toAsync(list2),
+);
+const res9 = pipe(
+  list2,
+  toAsync,
+  partition((a): a is number => typeof a === "number"),
+);
+
 checks([
-  check<typeof res1, [never[], never[]], Test.Pass>(),
-  check<typeof res2, Res, Test.Pass>(),
-  check<typeof res3, Res, Test.Pass>(),
-  check<typeof res4, Res, Test.Pass>(),
-  check<typeof res5, Promise<Res>, Test.Pass>(),
+  check<typeof res0, [never[], never[]], Test.Pass>(),
+  check<typeof res1, [number[], number[]], Test.Pass>(),
+  check<typeof res2, Res1, Test.Pass>(),
+  check<typeof res3, Res1, Test.Pass>(),
+  check<typeof res4, Res1, Test.Pass>(),
+  check<typeof res5, Promise<Res1>, Test.Pass>(),
+  check<typeof res6, Res2, Test.Pass>(),
+  check<typeof res7, Res2, Test.Pass>(),
+  check<typeof res8, Promise<Res2>, Test.Pass>(),
+  check<typeof res9, Promise<Res2>, Test.Pass>(),
 ]);