fp-pack 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +1 -0
  2. package/dist/fp-pack.umd.js +1 -1
  3. package/dist/fp-pack.umd.js.map +1 -1
  4. package/dist/implement/async/pipeAsync.d.ts +11 -0
  5. package/dist/implement/async/pipeAsync.d.ts.map +1 -1
  6. package/dist/implement/async/pipeAsync.mjs.map +1 -1
  7. package/dist/implement/async/pipeAsyncSideEffect.d.ts +11 -0
  8. package/dist/implement/async/pipeAsyncSideEffect.d.ts.map +1 -1
  9. package/dist/implement/async/pipeAsyncSideEffect.mjs.map +1 -1
  10. package/dist/implement/composition/compose.d.ts +25 -9
  11. package/dist/implement/composition/compose.d.ts.map +1 -1
  12. package/dist/implement/composition/compose.mjs.map +1 -1
  13. package/dist/implement/composition/compose.type-test.d.ts +35 -0
  14. package/dist/implement/composition/compose.type-test.d.ts.map +1 -0
  15. package/dist/implement/composition/compose.util.type-test.d.ts +31 -0
  16. package/dist/implement/composition/compose.util.type-test.d.ts.map +1 -0
  17. package/dist/implement/composition/index.d.ts +1 -0
  18. package/dist/implement/composition/index.d.ts.map +1 -1
  19. package/dist/implement/composition/pipe.type-test.d.ts +110 -0
  20. package/dist/implement/composition/pipe.type-test.d.ts.map +1 -1
  21. package/dist/implement/composition/pipe.util.type-test.d.ts +75 -0
  22. package/dist/implement/composition/pipe.util.type-test.d.ts.map +1 -0
  23. package/dist/implement/composition/tap0.d.ts +6 -0
  24. package/dist/implement/composition/tap0.d.ts.map +1 -0
  25. package/dist/implement/composition/tap0.mjs +9 -0
  26. package/dist/implement/composition/tap0.mjs.map +1 -0
  27. package/dist/index.mjs +230 -228
  28. package/dist/index.mjs.map +1 -1
  29. package/dist/skills/fp-pack/SKILL.md +2 -1
  30. package/dist/skills/fp-pack.md +2 -1
  31. package/package.json +1 -1
  32. package/src/implement/async/pipeAsync.ts +71 -0
  33. package/src/implement/async/pipeAsyncSideEffect.ts +74 -0
  34. package/src/implement/composition/compose.test.ts +14 -0
  35. package/src/implement/composition/compose.ts +133 -21
  36. package/src/implement/composition/compose.type-test.ts +109 -0
  37. package/src/implement/composition/compose.util.type-test.ts +128 -0
  38. package/src/implement/composition/index.ts +1 -0
  39. package/src/implement/composition/pipe.type-test.ts +252 -0
  40. package/src/implement/composition/pipe.util.type-test.ts +256 -0
  41. package/src/implement/composition/tap0.test.ts +16 -0
  42. package/src/implement/composition/tap0.ts +10 -0
@@ -5,6 +5,7 @@ import SideEffect, { isSideEffect } from '../composition/sideEffect';
5
5
  type MaybeSideEffect<T> = T | SideEffect<any>;
6
6
  type NonSideEffect<T> = Exclude<T, SideEffect<any>>;
7
7
  type AsyncOrSync<A, R> = (a: A) => MaybeSideEffect<R> | Promise<MaybeSideEffect<R>>;
8
+ type ZeroFn<R> = () => MaybeSideEffect<R> | Promise<MaybeSideEffect<R>>;
8
9
  type PipeInput<Fns extends AsyncOrSync<any, any>[]> = Fns extends [
9
10
  AsyncOrSync<infer A, any>,
10
11
  ...AsyncOrSync<any, any>[]
@@ -29,6 +30,79 @@ type PipeAsyncSideEffectFrom<Fns extends [FromFn<any>, ...AsyncOrSync<any, any>[
29
30
  function pipeAsyncSideEffect<Fns extends [FromFn<any>, ...AsyncOrSync<any, any>[]]>(
30
31
  ...funcs: Fns
31
32
  ): PipeAsyncSideEffectFrom<Fns>;
33
+ function pipeAsyncSideEffect<R>(ab: ZeroFn<R>): () => Promise<MaybeSideEffect<Awaited<R>>>;
34
+ function pipeAsyncSideEffect<B, R>(
35
+ ab: ZeroFn<B>,
36
+ bc: AsyncOrSync<Awaited<B>, R>
37
+ ): () => Promise<MaybeSideEffect<Awaited<R>>>;
38
+ function pipeAsyncSideEffect<B, C, R>(
39
+ ab: ZeroFn<B>,
40
+ bc: AsyncOrSync<Awaited<B>, C>,
41
+ cd: AsyncOrSync<Awaited<C>, R>
42
+ ): () => Promise<MaybeSideEffect<Awaited<R>>>;
43
+ function pipeAsyncSideEffect<B, C, D, R>(
44
+ ab: ZeroFn<B>,
45
+ bc: AsyncOrSync<Awaited<B>, C>,
46
+ cd: AsyncOrSync<Awaited<C>, D>,
47
+ de: AsyncOrSync<Awaited<D>, R>
48
+ ): () => Promise<MaybeSideEffect<Awaited<R>>>;
49
+ function pipeAsyncSideEffect<B, C, D, E, R>(
50
+ ab: ZeroFn<B>,
51
+ bc: AsyncOrSync<Awaited<B>, C>,
52
+ cd: AsyncOrSync<Awaited<C>, D>,
53
+ de: AsyncOrSync<Awaited<D>, E>,
54
+ ef: AsyncOrSync<Awaited<E>, R>
55
+ ): () => Promise<MaybeSideEffect<Awaited<R>>>;
56
+ function pipeAsyncSideEffect<B, C, D, E, F, R>(
57
+ ab: ZeroFn<B>,
58
+ bc: AsyncOrSync<Awaited<B>, C>,
59
+ cd: AsyncOrSync<Awaited<C>, D>,
60
+ de: AsyncOrSync<Awaited<D>, E>,
61
+ ef: AsyncOrSync<Awaited<E>, F>,
62
+ fg: AsyncOrSync<Awaited<F>, R>
63
+ ): () => Promise<MaybeSideEffect<Awaited<R>>>;
64
+ function pipeAsyncSideEffect<B, C, D, E, F, G, R>(
65
+ ab: ZeroFn<B>,
66
+ bc: AsyncOrSync<Awaited<B>, C>,
67
+ cd: AsyncOrSync<Awaited<C>, D>,
68
+ de: AsyncOrSync<Awaited<D>, E>,
69
+ ef: AsyncOrSync<Awaited<E>, F>,
70
+ fg: AsyncOrSync<Awaited<F>, G>,
71
+ gh: AsyncOrSync<Awaited<G>, R>
72
+ ): () => Promise<MaybeSideEffect<Awaited<R>>>;
73
+ function pipeAsyncSideEffect<B, C, D, E, F, G, H, R>(
74
+ ab: ZeroFn<B>,
75
+ bc: AsyncOrSync<Awaited<B>, C>,
76
+ cd: AsyncOrSync<Awaited<C>, D>,
77
+ de: AsyncOrSync<Awaited<D>, E>,
78
+ ef: AsyncOrSync<Awaited<E>, F>,
79
+ fg: AsyncOrSync<Awaited<F>, G>,
80
+ gh: AsyncOrSync<Awaited<G>, H>,
81
+ hi: AsyncOrSync<Awaited<H>, R>
82
+ ): () => Promise<MaybeSideEffect<Awaited<R>>>;
83
+ function pipeAsyncSideEffect<B, C, D, E, F, G, H, I, R>(
84
+ ab: ZeroFn<B>,
85
+ bc: AsyncOrSync<Awaited<B>, C>,
86
+ cd: AsyncOrSync<Awaited<C>, D>,
87
+ de: AsyncOrSync<Awaited<D>, E>,
88
+ ef: AsyncOrSync<Awaited<E>, F>,
89
+ fg: AsyncOrSync<Awaited<F>, G>,
90
+ gh: AsyncOrSync<Awaited<G>, H>,
91
+ hi: AsyncOrSync<Awaited<H>, I>,
92
+ ij: AsyncOrSync<Awaited<I>, R>
93
+ ): () => Promise<MaybeSideEffect<Awaited<R>>>;
94
+ function pipeAsyncSideEffect<B, C, D, E, F, G, H, I, J, R>(
95
+ ab: ZeroFn<B>,
96
+ bc: AsyncOrSync<Awaited<B>, C>,
97
+ cd: AsyncOrSync<Awaited<C>, D>,
98
+ de: AsyncOrSync<Awaited<D>, E>,
99
+ ef: AsyncOrSync<Awaited<E>, F>,
100
+ fg: AsyncOrSync<Awaited<F>, G>,
101
+ gh: AsyncOrSync<Awaited<G>, H>,
102
+ hi: AsyncOrSync<Awaited<H>, I>,
103
+ ij: AsyncOrSync<Awaited<I>, J>,
104
+ jk: AsyncOrSync<Awaited<J>, R>
105
+ ): () => Promise<MaybeSideEffect<Awaited<R>>>;
32
106
  function pipeAsyncSideEffect<A, R>(
33
107
  ab: AsyncOrSync<A, R>
34
108
  ): (a: A | SideEffect<any>) => Promise<MaybeSideEffect<Awaited<R>>>;
@@ -1,5 +1,6 @@
1
1
  import { describe, it, expect } from 'vitest';
2
2
  import compose from './compose';
3
+ import from from './from';
3
4
 
4
5
  describe('compose', () => {
5
6
  it('applies functions right-to-left', () => {
@@ -27,4 +28,17 @@ describe('compose', () => {
27
28
  expect(fn(4)).toBe(16);
28
29
  });
29
30
 
31
+ it('supports zero-arity tail functions', () => {
32
+ const addOne = (n: number) => n + 1;
33
+ const fn = compose(addOne, () => 2);
34
+ expect(fn()).toBe(3);
35
+ });
36
+
37
+ it('supports from() as a constant source', () => {
38
+ const addOne = (n: number) => n + 1;
39
+ const fn = compose(addOne, from(2));
40
+ expect(fn()).toBe(3);
41
+ expect(fn(10)).toBe(3);
42
+ });
43
+
30
44
  });
@@ -2,46 +2,158 @@
2
2
  * compose - 함수를 우→좌로 합성
3
3
  */
4
4
 
5
+ import type { FromFn } from './from';
6
+
5
7
  type UnaryFn<A, R> = (a: A) => R;
8
+ type ZeroFn<R> = () => R;
6
9
  type ComposeInput<Fns extends UnaryFn<any, any>[]> = Fns extends [...UnaryFn<any, any>[], UnaryFn<infer A, any>]
7
10
  ? A
8
11
  : never;
9
- type ComposeChain<Fns extends UnaryFn<any, any>[]> = Fns extends [UnaryFn<infer A, infer R>]
10
- ? UnaryFn<A, R>
12
+ type ComposeOutput<Fns extends UnaryFn<any, any>[]> = Fns extends [UnaryFn<any, infer R>]
13
+ ? R
11
14
  : Fns extends [UnaryFn<infer A, infer R>, ...infer Rest]
12
- ? Rest extends [UnaryFn<R, any>, ...UnaryFn<any, any>[]]
13
- ? ComposeChain<Rest> extends UnaryFn<any, infer NextR>
14
- ? UnaryFn<A, NextR>
15
+ ? Rest extends [UnaryFn<any, any>, ...UnaryFn<any, any>[]]
16
+ ? ComposeOutput<Rest> extends A
17
+ ? R
15
18
  : never
16
19
  : never
17
20
  : never;
18
- type Compose<Fns extends UnaryFn<any, any>[]> = (input: ComposeInput<Fns>) => ComposeChain<Fns> extends UnaryFn<
19
- any,
20
- infer R
21
- >
22
- ? R
23
- : never;
21
+ type Compose<Fns extends UnaryFn<any, any>[]> = (input: ComposeInput<Fns>) => ComposeOutput<Fns>;
22
+ type ComposeFrom<Fns extends [...UnaryFn<any, any>[], FromFn<any>]> = (input?: ComposeInput<Fns>) => ComposeOutput<Fns>;
23
+
24
+ function compose<R>(ab: ZeroFn<R>): () => R;
25
+ function compose<B, R>(ab: UnaryFn<B, R>, bc: ZeroFn<B>): () => R;
26
+ function compose<C, B, R>(ab: UnaryFn<B, R>, bc: UnaryFn<C, B>, cd: ZeroFn<C>): () => R;
27
+ function compose<D, C, B, R>(ab: UnaryFn<B, R>, bc: UnaryFn<C, B>, cd: UnaryFn<D, C>, de: ZeroFn<D>): () => R;
28
+ function compose<E, D, C, B, R>(
29
+ ab: UnaryFn<B, R>,
30
+ bc: UnaryFn<C, B>,
31
+ cd: UnaryFn<D, C>,
32
+ de: UnaryFn<E, D>,
33
+ ef: ZeroFn<E>
34
+ ): () => R;
35
+ function compose<F, E, D, C, B, R>(
36
+ ab: UnaryFn<B, R>,
37
+ bc: UnaryFn<C, B>,
38
+ cd: UnaryFn<D, C>,
39
+ de: UnaryFn<E, D>,
40
+ ef: UnaryFn<F, E>,
41
+ fg: ZeroFn<F>
42
+ ): () => R;
43
+ function compose<G, F, E, D, C, B, R>(
44
+ ab: UnaryFn<B, R>,
45
+ bc: UnaryFn<C, B>,
46
+ cd: UnaryFn<D, C>,
47
+ de: UnaryFn<E, D>,
48
+ ef: UnaryFn<F, E>,
49
+ fg: UnaryFn<G, F>,
50
+ gh: ZeroFn<G>
51
+ ): () => R;
52
+ function compose<H, G, F, E, D, C, B, R>(
53
+ ab: UnaryFn<B, R>,
54
+ bc: UnaryFn<C, B>,
55
+ cd: UnaryFn<D, C>,
56
+ de: UnaryFn<E, D>,
57
+ ef: UnaryFn<F, E>,
58
+ fg: UnaryFn<G, F>,
59
+ gh: UnaryFn<H, G>,
60
+ hi: ZeroFn<H>
61
+ ): () => R;
62
+ function compose<I, H, G, F, E, D, C, B, R>(
63
+ ab: UnaryFn<B, R>,
64
+ bc: UnaryFn<C, B>,
65
+ cd: UnaryFn<D, C>,
66
+ de: UnaryFn<E, D>,
67
+ ef: UnaryFn<F, E>,
68
+ fg: UnaryFn<G, F>,
69
+ gh: UnaryFn<H, G>,
70
+ hi: UnaryFn<I, H>,
71
+ ij: ZeroFn<I>
72
+ ): () => R;
73
+ function compose<J, I, H, G, F, E, D, C, B, R>(
74
+ ab: UnaryFn<B, R>,
75
+ bc: UnaryFn<C, B>,
76
+ cd: UnaryFn<D, C>,
77
+ de: UnaryFn<E, D>,
78
+ ef: UnaryFn<F, E>,
79
+ fg: UnaryFn<G, F>,
80
+ gh: UnaryFn<H, G>,
81
+ hi: UnaryFn<I, H>,
82
+ ij: UnaryFn<J, I>,
83
+ jk: ZeroFn<J>
84
+ ): () => R;
24
85
 
86
+ function compose<Fns extends [...UnaryFn<any, any>[], FromFn<any>]>(...funcs: Fns): ComposeFrom<Fns>;
25
87
  function compose<A, R>(ab: UnaryFn<A, R>): (a: A) => R;
26
- function compose<A, B, R>(ab: UnaryFn<A, B>, bc: UnaryFn<B, R>): (a: A) => R;
27
- function compose<A, B, C, R>(ab: UnaryFn<A, B>, bc: UnaryFn<B, C>, cd: UnaryFn<C, R>): (a: A) => R;
88
+ function compose<A, B, R>(ab: UnaryFn<B, R>, bc: UnaryFn<A, B>): (a: A) => R;
89
+ function compose<A, B, C, R>(ab: UnaryFn<C, R>, bc: UnaryFn<B, C>, cd: UnaryFn<A, B>): (a: A) => R;
28
90
  function compose<A, B, C, D, R>(
29
- ab: UnaryFn<A, B>,
30
- bc: UnaryFn<B, C>,
31
- cd: UnaryFn<C, D>,
32
- de: UnaryFn<D, R>
91
+ ab: UnaryFn<D, R>,
92
+ bc: UnaryFn<C, D>,
93
+ cd: UnaryFn<B, C>,
94
+ de: UnaryFn<A, B>
33
95
  ): (a: A) => R;
34
96
  function compose<A, B, C, D, E, R>(
35
- ab: UnaryFn<A, B>,
36
- bc: UnaryFn<B, C>,
97
+ ab: UnaryFn<E, R>,
98
+ bc: UnaryFn<D, E>,
37
99
  cd: UnaryFn<C, D>,
100
+ de: UnaryFn<B, C>,
101
+ ef: UnaryFn<A, B>
102
+ ): (a: A) => R;
103
+ function compose<A, B, C, D, E, F, R>(
104
+ ab: UnaryFn<F, R>,
105
+ bc: UnaryFn<E, F>,
106
+ cd: UnaryFn<D, E>,
107
+ de: UnaryFn<C, D>,
108
+ ef: UnaryFn<B, C>,
109
+ fg: UnaryFn<A, B>
110
+ ): (a: A) => R;
111
+ function compose<A, B, C, D, E, F, G, R>(
112
+ ab: UnaryFn<G, R>,
113
+ bc: UnaryFn<F, G>,
114
+ cd: UnaryFn<E, F>,
38
115
  de: UnaryFn<D, E>,
39
- ef: UnaryFn<E, R>
116
+ ef: UnaryFn<C, D>,
117
+ fg: UnaryFn<B, C>,
118
+ gh: UnaryFn<A, B>
119
+ ): (a: A) => R;
120
+ function compose<A, B, C, D, E, F, G, H, R>(
121
+ ab: UnaryFn<H, R>,
122
+ bc: UnaryFn<G, H>,
123
+ cd: UnaryFn<F, G>,
124
+ de: UnaryFn<E, F>,
125
+ ef: UnaryFn<D, E>,
126
+ fg: UnaryFn<C, D>,
127
+ gh: UnaryFn<B, C>,
128
+ hi: UnaryFn<A, B>
129
+ ): (a: A) => R;
130
+ function compose<A, B, C, D, E, F, G, H, I, R>(
131
+ ab: UnaryFn<I, R>,
132
+ bc: UnaryFn<H, I>,
133
+ cd: UnaryFn<G, H>,
134
+ de: UnaryFn<F, G>,
135
+ ef: UnaryFn<E, F>,
136
+ fg: UnaryFn<D, E>,
137
+ gh: UnaryFn<C, D>,
138
+ hi: UnaryFn<B, C>,
139
+ ij: UnaryFn<A, B>
140
+ ): (a: A) => R;
141
+ function compose<A, B, C, D, E, F, G, H, I, J, R>(
142
+ ab: UnaryFn<J, R>,
143
+ bc: UnaryFn<I, J>,
144
+ cd: UnaryFn<H, I>,
145
+ de: UnaryFn<G, H>,
146
+ ef: UnaryFn<F, G>,
147
+ fg: UnaryFn<E, F>,
148
+ gh: UnaryFn<D, E>,
149
+ hi: UnaryFn<C, D>,
150
+ ij: UnaryFn<B, C>,
151
+ jk: UnaryFn<A, B>
40
152
  ): (a: A) => R;
41
153
 
42
154
  function compose<Fns extends [UnaryFn<any, any>, ...UnaryFn<any, any>[]]>(...funcs: Fns): Compose<Fns>;
43
155
  function compose(...funcs: Array<UnaryFn<any, any>>): (input: any) => any;
44
- function compose(...funcs: Array<(input: any) => any>) {
156
+ function compose(...funcs: Array<(...args: any[]) => any>) {
45
157
  return (value: any) => funcs.reduceRight((acc, fn) => fn(acc), value);
46
158
  }
47
159
 
@@ -0,0 +1,109 @@
1
+ import compose from './compose';
2
+ import from from './from';
3
+
4
+ type Equal<A, B> = (<T>() => T extends A ? 1 : 2) extends (<T>() => T extends B ? 1 : 2)
5
+ ? true
6
+ : false;
7
+ type Expect<T extends true> = T;
8
+
9
+ export const pureCompose = compose(
10
+ (value: string) => `n:${value}`,
11
+ (value: number) => `${value}`,
12
+ (value: number) => value * 2,
13
+ (value: number) => value + 1
14
+ );
15
+
16
+ type PureComposeExpected = (input: number) => string;
17
+ export type ComposePureIsStrict = Expect<Equal<typeof pureCompose, PureComposeExpected>>;
18
+
19
+ export const pureComposeSix = compose(
20
+ (value: number) => `n:${value}`,
21
+ (value: number) => value + 1,
22
+ (value: string) => value.length,
23
+ (value: number) => `${value}`,
24
+ (value: number) => value * 2,
25
+ (value: number) => value + 1
26
+ );
27
+
28
+ type PureComposeSixExpected = (input: number) => string;
29
+ export type ComposePureSixIsStrict = Expect<Equal<typeof pureComposeSix, PureComposeSixExpected>>;
30
+
31
+ export const pureComposeTen = compose(
32
+ (value: string) => `n:${value}`,
33
+ (value: string) => value.padStart(4, '0'),
34
+ (value: number) => `${value}`,
35
+ (value: number) => value + 1,
36
+ (value: string) => value.length,
37
+ (value: string) => value.padStart(3, '0'),
38
+ (value: number) => `${value}`,
39
+ (value: number) => value * 2,
40
+ (value: number) => value + 1,
41
+ (value: number) => value + 1
42
+ );
43
+
44
+ type PureComposeTenExpected = (input: number) => string;
45
+ export type ComposePureTenIsStrict = Expect<Equal<typeof pureComposeTen, PureComposeTenExpected>>;
46
+
47
+ export const composeZero = compose(
48
+ (value: number) => value + 1,
49
+ (value: number) => value * 2,
50
+ () => 1
51
+ );
52
+
53
+ type ComposeZeroExpected = () => number;
54
+ export type ComposeZeroIsStrict = Expect<Equal<typeof composeZero, ComposeZeroExpected>>;
55
+
56
+ export const composeZeroValue = composeZero();
57
+
58
+ type ComposeZeroValueExpected = number;
59
+ export type ComposeZeroValueIsStrict = Expect<Equal<typeof composeZeroValue, ComposeZeroValueExpected>>;
60
+
61
+ export const composeFromNoInput = compose(from(1));
62
+
63
+ type ComposeFromNoInputExpected = (input?: unknown) => number;
64
+ export type ComposeFromNoInputIsStrict = Expect<Equal<typeof composeFromNoInput, ComposeFromNoInputExpected>>;
65
+
66
+ export const composeFromNoInputValue = composeFromNoInput();
67
+
68
+ type ComposeFromNoInputValueExpected = number;
69
+ export type ComposeFromNoInputValueIsStrict = Expect<
70
+ Equal<typeof composeFromNoInputValue, ComposeFromNoInputValueExpected>
71
+ >;
72
+
73
+ export const composeFromNoInputValueWithArg = composeFromNoInput('input');
74
+
75
+ type ComposeFromNoInputValueWithArgExpected = number;
76
+ export type ComposeFromNoInputValueWithArgIsStrict = Expect<
77
+ Equal<typeof composeFromNoInputValueWithArg, ComposeFromNoInputValueWithArgExpected>
78
+ >;
79
+
80
+ export const composeFromTen = compose(
81
+ (value: string) => `n:${value}`,
82
+ (value: string) => value.padStart(4, '0'),
83
+ (value: number) => `${value}`,
84
+ (value: number) => value + 1,
85
+ (value: string) => value.length,
86
+ (value: string) => value.padStart(3, '0'),
87
+ (value: number) => `${value}`,
88
+ (value: number) => value * 2,
89
+ (value: number) => value + 1,
90
+ from(1)
91
+ );
92
+
93
+ export const composeFromTenValue = composeFromTen('input');
94
+
95
+ type ComposeFromTenValueExpected = string;
96
+ export type ComposeFromTenValueIsStrict = Expect<Equal<typeof composeFromTenValue, ComposeFromTenValueExpected>>;
97
+
98
+ export const composeFromTenValueNoInput = composeFromTen();
99
+
100
+ type ComposeFromTenValueNoInputExpected = string;
101
+ export type ComposeFromTenValueNoInputIsStrict = Expect<
102
+ Equal<typeof composeFromTenValueNoInput, ComposeFromTenValueNoInputExpected>
103
+ >;
104
+
105
+ // Negative cases: input required when not using from/zero-arity.
106
+ // @ts-expect-error input required for unary compose
107
+ pureCompose();
108
+ // @ts-expect-error input required for direct compose call
109
+ compose((value: number) => value + 1)();
@@ -0,0 +1,128 @@
1
+ import compose from './compose';
2
+ import filter from '../array/filter';
3
+ import flattenDeep from '../array/flattenDeep';
4
+ import map from '../array/map';
5
+ import sum from '../math/sum';
6
+ import gt from '../equality/gt';
7
+ import when from '../control/when';
8
+ import ifElse from '../control/ifElse';
9
+ import log from '../debug/log';
10
+ import tap from './tap';
11
+ import prop from '../object/prop';
12
+ import mergeAll from '../object/mergeAll';
13
+ import getOrElse from '../nullable/getOrElse';
14
+ import mapMaybe from '../nullable/mapMaybe';
15
+ import join from '../string/join';
16
+ import trim from '../string/trim';
17
+ import toUpper from '../string/toUpper';
18
+ import streamFilter from '../../stream/filter';
19
+ import streamMap from '../../stream/map';
20
+ import streamRange from '../../stream/range';
21
+ import streamReduce from '../../stream/reduce';
22
+ import streamToArray from '../../stream/toArray';
23
+
24
+ type Equal<A, B> = (<T>() => T extends A ? 1 : 2) extends (<T>() => T extends B ? 1 : 2)
25
+ ? true
26
+ : false;
27
+ type Expect<T extends true> = T;
28
+
29
+ type User = {
30
+ name?: string;
31
+ };
32
+
33
+ type TagOwner = {
34
+ tags?: Array<string | null>;
35
+ };
36
+
37
+ type Config = {
38
+ value?: number;
39
+ };
40
+
41
+ const getName = prop('name') as (user: User) => string | undefined;
42
+ const getTags = prop('tags') as (owner: TagOwner) => Array<string | null> | undefined;
43
+ const mergeConfig = mergeAll as (configs: Config[]) => Config;
44
+ const getValue = prop('value') as (config: Config) => number | undefined;
45
+ const streamMapNumber = streamMap((value: number) => value + 1) as (iterable: Iterable<number>) => IterableIterator<number>;
46
+ const streamFilterNumber = streamFilter((value: number) => value > 1) as (
47
+ iterable: Iterable<number>
48
+ ) => IterableIterator<number>;
49
+ const streamReduceNumber = streamReduce((acc: number, value: number) => acc + value, 0) as (
50
+ iterable: Iterable<number>
51
+ ) => number;
52
+ const streamMapAsyncNumber = streamMap(async (value: number) => value + 1) as (
53
+ iterable: Iterable<number>
54
+ ) => IterableIterator<Promise<number>>;
55
+ const whenPositive = when<number>(gt(0), (value) => value + 1);
56
+
57
+ export const composeArrayControlString = compose(
58
+ log('total'),
59
+ toUpper,
60
+ trim,
61
+ (value: number) => `${value}`,
62
+ when(gt(10), (value) => value + 1),
63
+ sum,
64
+ map((value: number) => value * 2),
65
+ filter(gt(1)),
66
+ flattenDeep<number>
67
+ );
68
+
69
+ type ComposeArrayControlStringExpected = (input: any[]) => string;
70
+ export type ComposeArrayControlStringIsStrict = Expect<
71
+ Equal<typeof composeArrayControlString, ComposeArrayControlStringExpected>
72
+ >;
73
+
74
+ export const composeObjectNullableControl = compose(
75
+ tap((value: string) => value.length),
76
+ ifElse(
77
+ (value: string) => value.length > 0,
78
+ toUpper,
79
+ () => 'UNKNOWN'
80
+ ),
81
+ getOrElse(''),
82
+ getName
83
+ );
84
+
85
+ type ComposeObjectNullableControlExpected = (input: User) => string;
86
+ export type ComposeObjectNullableControlIsStrict = Expect<
87
+ Equal<typeof composeObjectNullableControl, ComposeObjectNullableControlExpected>
88
+ >;
89
+
90
+ export const composeTags = compose(
91
+ join('|'),
92
+ map((tag: string) => tag.toUpperCase()),
93
+ mapMaybe((tag: string | null) => (tag ? tag.trim() : null)),
94
+ getOrElse<Array<string | null>>([]),
95
+ getTags
96
+ );
97
+
98
+ type ComposeTagsExpected = (input: TagOwner) => string;
99
+ export type ComposeTagsIsStrict = Expect<Equal<typeof composeTags, ComposeTagsExpected>>;
100
+
101
+ export const composeConfigValue = compose(
102
+ whenPositive,
103
+ getOrElse(0),
104
+ getValue,
105
+ mergeConfig
106
+ );
107
+
108
+ type ComposeConfigValueExpected = (input: Config[]) => number;
109
+ export type ComposeConfigValueIsStrict = Expect<Equal<typeof composeConfigValue, ComposeConfigValueExpected>>;
110
+
111
+ export const composeStreamSync = compose(
112
+ streamReduceNumber,
113
+ streamFilterNumber,
114
+ streamMapNumber,
115
+ (end: number) => streamRange(0, end)
116
+ );
117
+
118
+ type ComposeStreamSyncExpected = (input: number) => number;
119
+ export type ComposeStreamSyncIsStrict = Expect<Equal<typeof composeStreamSync, ComposeStreamSyncExpected>>;
120
+
121
+ export const composeStreamToArray = compose(
122
+ streamToArray,
123
+ streamMapAsyncNumber,
124
+ (end: number) => streamRange(0, end)
125
+ );
126
+
127
+ type ComposeStreamToArrayExpected = (input: number) => Promise<number[]>;
128
+ export type ComposeStreamToArrayIsStrict = Expect<Equal<typeof composeStreamToArray, ComposeStreamToArrayExpected>>;
@@ -12,6 +12,7 @@ export { default as constant } from './constant';
12
12
  export { default as from } from './from';
13
13
  export type { FromFn } from './from';
14
14
  export { default as tap } from './tap';
15
+ export { default as tap0 } from './tap0';
15
16
  export { default as once } from './once';
16
17
  export { default as memoize } from './memoize';
17
18
  export { default as SideEffect } from './sideEffect';