TypeScript Tidbit: How to Overload an Imported Function

November 20, 2020 - 5 min

There are times when the type definitions shipped on DefinitelyTyped are not enough and this results in making the TypeScript compiler unhappy. Oh noes! 😭

Man overloaded with post-it notes
Functions Overloading Be Like, Photo by Luis Villasmil) on Unsplash.

Overload an imported function in TypeScript

But fear not! It is possible to overload a function imported from a different module (which could even be a local module/file, not necessarily a node module) without “patching” the corresponding @types/<package> type definitions.

import { someFunction } from 'module'

// the function overloading
declare module 'module' {
  function someFunction() // the overload you need
}

someFunction() // with the overloaded version ✔️

Example: overload Ramda’s compose function

A practical example is Ramda’s compose function. If you pass more than 7 functions to the function, you need to overload Ramda’s type definitions coming from @types/ramda as the TypeScript compiler will report an error otherwise (TypeScript Playground Error Example).

Example TypeScript error: Expected 1-6 arguments, but got 7.

DefinitelyTyped obviously cannot provide the type definitions for any number of arguments as compose accepts as many arguments as you want. So it’s up to you to inform the TypeScript compiler that this usage is actually okay. 😌

This can be done like in the example below (TypeScript Playground Working Example):

import * as R from 'ramda'

// overloading compose because `@types/ramda` exposes types for up to 6 functions
declare module 'ramda' {
  function compose<V0, T1, T2, T3, T4, T5, T6, T7>(
    fn6: (x: T6) => T7,
    fn5: (x: T5) => T6,
    fn4: (x: T4) => T5,
    fn3: (x: T3) => T4,
    fn2: (x: T2) => T3,
    fn1: (x: T1) => T2,
    fn0: (x0: V0) => T1
  ): (x0: V0) => T7
}

// and now, we can use:
const someComplicatedResult = R.compose<V0, T1, T2, T3, T4, T5, T6, T7>(
  fn6,
  fn5,
  fn4,
  fn3,
  fn2,
  fn1,
  fn0
)('😅')

We can see from the screenshot below that the overload was correctly taken into consideration. 🎉

IntelliSense popup showing successful overload (see TS playground)

Caveat

I can imagine that if this is overused, things can become quickly intractable. So use it sparingly and always with a good reason why. Your future self will be happy to find an explanation in a comment above the overloading as it is kind of unusual. 😉


I discovered this while refactoring @kiwicom/splitster to TS (link to commit) and thought it might be useful to share. Hope it helped you!

Join Robin's Gazette

Receive

every second Wednesday

my favourite links and findings

on frontend technologies, writing and more!

Disclaimer: I'm completely new to this 😅

    I hate spam, don't expect any from me. Unsubscribe at any time.



    Personal blog written by Robin Cussol
    I like math and I like code. Oh, and writing too.