-
Notifications
You must be signed in to change notification settings - Fork 0
/
curry.ts
108 lines (96 loc) · 2.4 KB
/
curry.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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
// Copyright 2023-latest Tomoki Miyauchi. All rights reserved. MIT license.
// This module is browser compatible.
// deno-lint-ignore-file no-explicit-any
/**
* Alias for Any array types
*
* @internal
*/
type AnyArray = readonly unknown[];
/**
* @internal
*/
type Union2Intersection<U> = (
U extends unknown ? (arg: U) => void : never
) extends (arg: infer I) => void ? I
: never;
/**
* Pop types
* @typeParams - T Any array
*
* @internal
*/
type Pop<T extends AnyArray> = T extends [...infer Head, unknown] ? Head
: T extends readonly [...infer Head, unknown] ? readonly [...Head]
: never;
/**
* Shift types
* @typeParams S - Any array
*
* @internal
*/
type Shift<S extends AnyArray, T extends AnyArray> = T extends [
...S,
...infer Rest,
] ? Rest
: never;
/**
* @internal
*/
type UnionFactorial<T extends AnyArray> = T extends readonly [] ? never
: T | UnionFactorial<Pop<T>>;
/**
* @internal
*/
type OverloadsByArgs<
Args extends AnyArray,
FullArgs extends AnyArray,
ReturnValue,
> = Args extends unknown
? CurriedWithFixArgs<Args, Shift<Args, FullArgs>, ReturnValue>
: never;
/**
* @internal
*/
type CurriedWithFixArgs<
Args extends AnyArray,
RestArgs extends AnyArray,
ReturnValue,
> = (...args: Args) => Curried<RestArgs, ReturnValue>;
/**
* Curry types
*
* @internal
*/
type Curried<Args extends AnyArray, ReturnValue> = Args extends [] ? ReturnValue
: Union2Intersection<
OverloadsByArgs<UnionFactorial<Args>, Args, ReturnValue>
>;
/**
* Return curried function.
*
* @param fn - The function to curry
* @returns The new curried function
*
* @remarks
* Maximum number of arity is `19`. Beyond that, the type system will breaks.
*
* @example
* ```ts
* import { curry } from "https://deno.land/x/curry@$VERSION/mod.ts";
* const replace = (from: string, to: string, val: string) => val.replace(from, to)
* const curriedReplace = curry(replace)
* curriedReplace('hello', 'hi', 'hello world') // 'hi world'
* curriedReplace('hello')('hi', 'hello world') // 'hi world'
* curriedReplace('hello')('hi')('hello world') // 'hi world'
* ```
*/
const curry = <T extends unknown[], R>(
fn: (...args: T) => R,
): T["length"] extends 0 ? () => R : Curried<T, R> => {
const curried: any = (...t: T) =>
t.length >= fn.length ? fn(...t) : curried.bind(null, ...t);
return curried;
};
export { curry };
export type { Pop, Shift, Union2Intersection, UnionFactorial };