From 4c2ede2030232ae13896aae9a4221684d3ae198e Mon Sep 17 00:00:00 2001 From: fResult Sila Date: Mon, 4 Dec 2023 17:58:24 +0700 Subject: [PATCH] fix: Improve type inference for `chain` function --- src/curry.ts | 51 +++++++++++++++++++++++++++++++++++++---- src/tests/curry.test.ts | 47 ++++++++++++++++++++++++++++++++++++- 2 files changed, 93 insertions(+), 5 deletions(-) diff --git a/src/curry.ts b/src/curry.ts index 7bb091be..eaaf6984 100644 --- a/src/curry.ts +++ b/src/curry.ts @@ -1,12 +1,55 @@ +export type UnaryFunc = (arg: T) => R export type Func = ( ...args: TArgs[] ) => KReturn -export const chain = - (...funcs: Func[]) => - (...args: any[]) => { - return funcs.slice(1).reduce((acc, fn) => fn(acc), funcs[0](...args)) +export function chain( + fn1: UnaryFunc, + fn2: UnaryFunc +): UnaryFunc +export function chain( + fn1: UnaryFunc, + fn2: UnaryFunc, + fn3: UnaryFunc +): UnaryFunc +export function chain( + fn1: UnaryFunc, + fn2: UnaryFunc, + fn3: UnaryFunc, + fn4: UnaryFunc +): UnaryFunc +export function chain( + fn1: UnaryFunc, + fn2: UnaryFunc, + fn3: UnaryFunc, + fn4: UnaryFunc, + fn5: UnaryFunc +): UnaryFunc +export function chain( + fn1: UnaryFunc, + fn2: UnaryFunc, + fn3: UnaryFunc, + fn4: UnaryFunc, + fn5: UnaryFunc, + fn6: UnaryFunc +): UnaryFunc +export function chain( + fn1: UnaryFunc, + fn2: UnaryFunc, + fn3: UnaryFunc, + fn4: UnaryFunc, + fn5: UnaryFunc, + fn6: UnaryFunc, + fn7: UnaryFunc +): UnaryFunc +export function chain( + ...fns: ((arg: any) => any)[] +): UnaryFunc +export function chain(...funcs: Func[]): Func { + return function forX(initialParam: Parameters[0]) { + return funcs.reduce((acc, fn) => fn(acc), initialParam) } +} export const compose = (...funcs: Func[]) => { return funcs.reverse().reduce((acc, fn) => fn(acc)) diff --git a/src/tests/curry.test.ts b/src/tests/curry.test.ts index cf63aa37..550a0986 100644 --- a/src/tests/curry.test.ts +++ b/src/tests/curry.test.ts @@ -94,9 +94,54 @@ describe('curry module', () => { const addFive = (num: number) => num + 5 const twoX = (num: number) => num * 2 const func = _.chain(genesis, addFive, twoX) - const result = func() + const result = func(0) assert.equal(result, 10) }) + + test('calls add(1), then addFive, then twoX functions by 1', () => { + const add = (y: number) => (x: number) => x + y + const addFive = (num: number) => num + 5 + const twoX = (num: number) => num * 2 + const func = _.chain(add(1), addFive, twoX) + const result = func(1) + assert.equal(result, 14) + }) + + test('calls add(2), then addFive, then twoX, then repeatX functions by 1', () => { + const add = (y: number) => (x: number) => x + y + const addFive = (num: number) => num + 5 + const twoX = (num: number) => num * 2 + const repeatX = (num: number) => 'X'.repeat(num) + const func = _.chain(add(2), addFive, twoX, repeatX) + const result = func(1) + assert.equal(result, 'XXXXXXXXXXXXXXXX') + }) + + test('calls addFive, then add(2), then twoX, then repeatX functions by 1', () => { + const add = (y: number) => (x: number) => x + y + const addFive = (num: number) => num + 5 + const twoX = (num: number) => num * 2 + const repeatX = (num: number) => 'X'.repeat(num) + const func = _.chain(addFive, add(2), twoX, repeatX) + const result = func(1) + assert.equal(result, 'XXXXXXXXXXXXXXXX') + }) + + test('calls getName, then upperCase functions as a mapper for User[]', () => { + type User = { id: number; name: string } + const users: User[] = [ + { id: 1, name: 'John Doe' }, + { id: 2, name: 'John Smith' }, + { id: 3, name: 'John Wick' } + ] + const getName = (item: T) => item.name + const upperCase: (x: string) => Uppercase = (text: string) => + text.toUpperCase() as Uppercase + + const getUpperName = _.chain>(getName, upperCase) + const result = users.map(getUpperName) + assert.deepEqual(result, ['JOHN DOE', 'JOHN SMITH', 'JOHN WICK']) + }) }) describe('proxied function', () => {