From 1bed975a10d9c1c3d430e51897a332a1be2f2c02 Mon Sep 17 00:00:00 2001
From: uri <uri.valevski@gmail.com>
Date: Tue, 7 Jan 2025 03:40:33 +0200
Subject: [PATCH] improve-async-detection

---
 src/composition.test.ts | 15 +++++++++++++--
 src/typing.ts           | 10 ++++++++--
 2 files changed, 21 insertions(+), 4 deletions(-)

diff --git a/src/composition.test.ts b/src/composition.test.ts
index 7ae3dff..81eb876 100644
--- a/src/composition.test.ts
+++ b/src/composition.test.ts
@@ -87,11 +87,22 @@ const _1 = <T, Fn extends (x: T) => number>(f: Fn) => {
   pipe((x: T) => x, f);
 };
 
+// Generics does not make everything async.
+const _2: number = pipe(<T extends number>(x: T) => x, (y: number) => y)(1);
+
+// Generics extends Promise is considered async.
+const _3: number = await pipe(
+  <T extends number>(x: T) => Promise.resolve(x),
+  (y: number) => y,
+)(1);
+
 // failing typing tests:
 
-// const _2 = pipe(<T extends number>(x: T) => x, (y: number) => y)(1) + 7;
+// Generics can infer by the last function
+// const _4: number = pipe((y: number) => y, <T >(x: T) => x)(1);
 
-// const _3 = <Fn extends (x: string) => number>(f: Fn) => {
+// Generics understands contextual extends
+// const _5 = <Fn extends (x: string) => number>(f: Fn) => {
 //   // @ts-expect-error first function does not match second
 //   pipe((x: number) => x, f);
 // };
diff --git a/src/typing.ts b/src/typing.ts
index b8f369e..4fd4cb4 100644
--- a/src/typing.ts
+++ b/src/typing.ts
@@ -4,9 +4,15 @@ export type Union<T extends any[]> = T[number];
 // deno-lint-ignore no-explicit-any
 export type AsyncFunction = (...args: any[]) => Promise<any>;
 
-export type AnyAsync<Functions> = Functions extends [infer f, ...infer rest]
+export type AnyAsync<Functions> = Functions extends [infer F, ...infer Rest]
   // deno-lint-ignore no-explicit-any
-  ? f extends AsyncFunction ? any : AnyAsync<rest>
+  ? F extends (...args: any[]) => infer R // Extract return type
+    // deno-lint-ignore no-explicit-any
+    ? R extends Promise<any> // Check if it returns a Promise
+      // deno-lint-ignore no-explicit-any
+      ? any
+    : AnyAsync<Rest>
+  : AnyAsync<Rest>
   : never;
 
 export type Unary<Input, Output> = (_: Input) => Output;