sonhanguyen
9/17/2019 - 12:19 AM

OverloadBuilder

OverloadBuilder

type Func = (..._) => any

const overload = <Default extends Func>(defaultFunc: Default): OverloadBuilder<Default> => {
  // @ts-ignore
  const match = cases => Object.assign(
    (...args) => {
      const matcher = cases.find(([ guard ]) => guard(args))
      const func = matcher
        ? matcher[1]
        : defaultFunc

      return func(...args)
    },
    { case: (matcher, handler) =>
      match([ ...cases, [ matcher, handler ] ])
    }
  )

  return match([]) as any
}

type OverloadBuilder<Default extends Func> = Default & {
  case<Overload extends Func>(
    guard: (args: any) => args is Parameters<Overload>,
    handler: Overload
  ): OverloadBuilder<Default & Overload>
}