Function currying with static type checks

I needed to curry a function in Typescript to simplify code, but with the static type checks in place. I explored currying from Rambda, Lodash and also the vanilla fat-arrow syntax. Both the Lodash and the fat-arrow approach ensured proper types, Rambda didn't not:

interface Foo {}
interface Bar {}
type Trigger = "created" | "updated"
const foos = [] as Foo[]

// function we'd like to curry...
const fooToBar = (trigger: Trigger, foo: Foo): Bar => ({})
// ...to simplify this map
const bars = foos.map((foo) => fooToBar("created", foo))

// Rambda
import R from "rambda"
const fooToBarCurriedRambda = R.curry(fooToBar)
// 😥 this compiles, but should not: number is not Foo
foos.map(fooToBarCurriedRambda(1))

// Lodash
import { curry } from "lodash"
const fooToBarCurriedLodash = curry(fooToBar)
// 🎉 compilation error: number is not assignable to Foo
foos.map(fooToBarCurriedLodash(1))
// 🎉 compilation error: Bar[] not assignable to number[]
const foosLodash: number[] = foos.map(fooToBarCurriedLodash("created"))

// vanilla Typescript with fat arrows
const fooToBarCurriedFatArrow = (trigger: Trigger) => (foo: Foo): Bar => ({})
// 🎉 compilation error: number is not assignable to Foo
foos.map(fooToBarCurriedFatArrow(1))
// 🎉 compilation error: Bar[] not assignable to number[]
const foosFatArrow: number[] = foos.map(fooToBarCurriedFatArrow("created"))

For now, I went with the fat arrow approach. I've also stumbled upon the bind approach that I haven't explored yet.


Would you like to connect? Subscribe via email or RSS , or follow me on Twitter!