# curryRecord

This is an example of a function whose logic lives in the type definition rather than in the function body.

## Breaking it down

`export const curryRecord = <F extends (x: Record<any, any>) => any>(fn: F) => ...`

`export const curryRecord = <F extends (x: Record<any, any>) => any>(fn: F) => ...`

The first argument is a unary function. We use the generic `<F extends...>`

to enforce arity and to enforce the passed function's argument being a record. The exact type of `x`

and the return type will be inferred by Typescript once a function is actually passed.

`<T extends Partial<Parameters<F>[0]>>(a: T) => ...`

`<T extends Partial<Parameters<F>[0]>>(a: T) => ...`

Once we pass our unary function we get a new function that takes `a: T`

. `T`

must be assignable to `Partial<Parameters<F>[0]>`

, which means "a partial version of whatever record your unary function expects as an argument."

`(b: Omit<Parameters<F>[0], keyof T>): ...`

`(b: Omit<Parameters<F>[0], keyof T>): ...`

Once we pass our partial record, we get a new function that takes a record `b`

that must contain all keys of `Parameters<F>[0]`

that were NOT included in `a`

. It checks this by omitting all keys of `a`

from `Parameters<F>[0]`

: the remaining keys are the keys that must be passed as a part of `b`

.

`ReturnType<F>`

`ReturnType<F>`

The return type of the curried function should match the return type of the function we're currying in the first place. We don't know what this is, so we use `ReturnType<F>`

to infer the proper type once a function is actually passed.

`... => fn({ ...a, ...b } as Parameters<F>[0])`

`... => fn({ ...a, ...b } as Parameters<F>[0])`

We trivially spread both partial arguments into the original function. We have to coerce this here because Typescript can't figure out that the above constraints means that `{ ...a, ...b }`

will always be of type `Parameters<F>[0]`

.

Interestingly enough, once compiled to Javascript this function becomes `curryRecord => fn => a => b => fn({ ...a, ...b })`

, which does absolutely zero validation. All the logic is handled by the type system!

## Demo and test cases

Last updated