generated from b00gizm/nodejs-typescript-template
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.ts
79 lines (72 loc) · 2.97 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
/* eslint-disable @typescript-eslint/no-explicit-any */
type First<T extends unknown[]> = T extends [infer F, ...unknown[]] ? F : never
type Last<T extends unknown[]> = T extends [...unknown[], infer L] ? L : never
type Tail<T extends unknown[]> = T extends [unknown, ...infer R] ? R : never
type ArgTypes<F> = F extends (...args: infer A) => unknown ? A : never
type FirstArgType<F> = F extends (arg: infer A, ...rest: any) => unknown
? A
: never
type LookUp<T, K extends keyof any, Default = never> = K extends keyof T
? T[K]
: Default
type LaxReturnType<F> = F extends (...args: any) => infer R ? R : never
type AnyFunc = (...arg: any) => any
type AnyAsyncFunc = (...arg: any) => Promise<any>
type IsAsyncFunc<F> = F extends (...args: any) => Promise<any> ? true : false
type Chain<
FType extends AnyFunc | AnyAsyncFunc,
Left extends [FType, ...FType[]],
Right extends FType[] = Tail<Left>,
> = {
[LKey in keyof Left]: (
...args: ArgTypes<Left[LKey]>
) => IsAsyncFunc<Left[LKey]> extends true
? Promise<FirstArgType<LookUp<Right, LKey, any>>>
: FirstArgType<LookUp<Right, LKey, any>>
}
/**
* Function to chain multiple functions together. The return type of each function
* must match the (first) argument type of the next function in the chain.
*
* If there are more than one argument in the first function, the the second to
* last arguments will be passed to the second function, and so on.
*
* @typedef {function(any): any} AnyFunc - Any function
* @param {...AnyFunc} funcs - The functions to chain together
* @returns {AnyFunc} A function that will execute the chain of functions
*/
export function chain<LType extends [AnyFunc, ...AnyFunc[]]>(
...funcs: LType & Chain<AnyFunc, LType>
): (...args: ArgTypes<First<LType>>) => LaxReturnType<Last<LType>> {
return (...args: ArgTypes<First<LType>>): LaxReturnType<Last<LType>> => {
const [first] = funcs.reduce((accumulator, nextFunc) => {
const [first, ...rest] = accumulator
const result = nextFunc(first, ...rest)
return [result, ...rest]
}, args as any)
return first as LaxReturnType<Last<LType>>
}
}
/**
* Asynchronous variant of {@link chain}.
*
* @typedef {function(...any): Promise<any>} AnyAsyncFunc - Any async function
* @param {...AnyAsyncFunc} funcs - The async functions to chain together
* @returns {AnyAsyncFunc} A function that will execute the chain of async functions
*/
export function asyncChain<LType extends [AnyAsyncFunc, ...AnyAsyncFunc[]]>(
...funcs: LType & Chain<AnyAsyncFunc, LType>
): (
...args: ArgTypes<First<LType>>
) => Promise<Awaited<LaxReturnType<Last<LType>>>> {
return async (
...args: ArgTypes<First<LType>>
): Promise<Awaited<LaxReturnType<Last<LType>>>> => {
const [first] = await funcs.reduce(async (accumulator, nextFunc) => {
const [first, ...rest] = await accumulator
const result = await nextFunc(first, ...rest)
return [result, ...rest]
}, args as any)
return first as Awaited<LaxReturnType<Last<LType>>>
}
}