Skip to content

Commit 2002604

Browse files
committed
fix: Improve type inference for chain function
1 parent 03dd315 commit 2002604

File tree

2 files changed

+58
-5
lines changed

2 files changed

+58
-5
lines changed

src/curry.ts

+12-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
1+
export type UnaryFunc<T, R> = (arg: T) => R
12
export type Func<TArgs = any, KReturn = any | void> = (
23
...args: TArgs[]
34
) => KReturn
45

5-
export const chain =
6-
(...funcs: Func[]) =>
7-
(...args: any[]) => {
8-
return funcs.slice(1).reduce((acc, fn) => fn(acc), funcs[0](...args))
6+
export function chain<T1, T2, R>(fn1: UnaryFunc<T1, T2>, fn2: UnaryFunc<T2, R>): UnaryFunc<T1, R>;
7+
export function chain<T1, T2, T3, R>(fn1: UnaryFunc<T1, T2>, fn2: UnaryFunc<T2, T3>, fn3: UnaryFunc<T3, R>): UnaryFunc<T1, R>;
8+
export function chain<T1, T2, T3, T4, R>(fn1: UnaryFunc<T1, T2>, fn2: UnaryFunc<T2, T3>, fn3: UnaryFunc<T3, T4>, fn4: UnaryFunc<T4, R>): UnaryFunc<T1, R>;
9+
export function chain<T1, T2, T3, T4, T5, R>(fn1: UnaryFunc<T1, T2>, fn2: UnaryFunc<T2, T3>, fn3: UnaryFunc<T3, T4>, fn4: UnaryFunc<T4, T5>, fn5: UnaryFunc<T5, R>): UnaryFunc<T1, R>;
10+
export function chain<T1, T2, T3, T4, T5, T6, R>(fn1: UnaryFunc<T1, T2>, fn2: UnaryFunc<T2, T3>, fn3: UnaryFunc<T3, T4>, fn4: UnaryFunc<T4, T5>, fn5: UnaryFunc<T5, T6>, fn6: UnaryFunc<T6, R>): UnaryFunc<T1, R>;
11+
export function chain<T1, T2, T3, T4, T5, T6, T7, R>(fn1: UnaryFunc<T1, T2>, fn2: UnaryFunc<T2, T3>, fn3: UnaryFunc<T3, T4>, fn4: UnaryFunc<T4, T5>, fn5: UnaryFunc<T5, T6>, fn6: UnaryFunc<T6, T7>, fn7: UnaryFunc<T7, R>): UnaryFunc<T1, R>;
12+
export function chain<T = any, R = any>(...fns: ((arg: any) => any)[]): UnaryFunc<T, R>;
13+
export function chain(...funcs: Func[]): Func {
14+
return function forX(initialParam: Parameters<Func>[0]) {
15+
return funcs.reduce((acc, fn) => fn(acc), initialParam)
916
}
17+
}
1018

1119
export const compose = (...funcs: Func[]) => {
1220
return funcs.reverse().reduce((acc, fn) => fn(acc))

src/tests/curry.test.ts

+46-1
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,54 @@ describe('curry module', () => {
9494
const addFive = (num: number) => num + 5
9595
const twoX = (num: number) => num * 2
9696
const func = _.chain(genesis, addFive, twoX)
97-
const result = func()
97+
const result = func(0)
9898
assert.equal(result, 10)
9999
})
100+
101+
test('calls add(1), then addFive, then twoX functions by 1', () => {
102+
const add = (y: number) => (x: number) => x + y
103+
const addFive = (num: number) => num + 5
104+
const twoX = (num: number) => num * 2
105+
const func = _.chain(add(1), addFive, twoX)
106+
const result = func(1)
107+
assert.equal(result, 14)
108+
})
109+
110+
test('calls add(2), then addFive, then twoX, then repeatX functions by 1', () => {
111+
const add = (y: number) => (x: number) => x + y
112+
const addFive = (num: number) => num + 5
113+
const twoX = (num: number) => num * 2
114+
const repeatX = (num: number) => 'X'.repeat(num)
115+
const func = _.chain(add(2), addFive, twoX, repeatX)
116+
const result = func(1)
117+
assert.equal(result, 'XXXXXXXXXXXXXXXX')
118+
})
119+
120+
test('calls addFive, then add(2), then twoX, then repeatX functions by 1', () => {
121+
const add = (y: number) => (x: number) => x + y
122+
const addFive = (num: number) => num + 5
123+
const twoX = (num: number) => num * 2
124+
const repeatX = (num: number) => 'X'.repeat(num)
125+
const func = _.chain(addFive, add(2), twoX, repeatX)
126+
const result = func(1)
127+
assert.equal(result, 'XXXXXXXXXXXXXXXX')
128+
})
129+
130+
test('calls getName, then upperCase functions as a mapper for User[]', () => {
131+
type User = { id: number; name: string }
132+
const users: User[] = [
133+
{ id: 1, name: 'John Doe' },
134+
{ id: 2, name: 'John Smith' },
135+
{ id: 3, name: 'John Wick' }
136+
]
137+
const getName = <T extends { name: string }>(item: T) => item.name
138+
const upperCase: (x: string) => Uppercase<string> = (text: string) =>
139+
text.toUpperCase() as Uppercase<string>
140+
141+
const getUpperName = _.chain<User, Uppercase<string>>(getName, upperCase)
142+
const result = users.map(getUpperName)
143+
assert.deepEqual(result, ['JOHN DOE', 'JOHN SMITH', 'JOHN WICK'])
144+
})
100145
})
101146

102147
describe('proxied function', () => {

0 commit comments

Comments
 (0)