UNPKG

pragmatic-fp-ts

Version:

Opinionated functional programming library with easy use in mind

47 lines (40 loc) 1.62 kB
import { Dictionary, Mappable } from "./types.ts"; type TMultiMethod<A, B> = A extends any[] ? (...args: A) => B : Mappable<A, B>; // Implemented as interface + class only to have better TS types export interface MultiMethod<ArgList extends any[], B> { (...args: ArgList): B; } export class MultiMethod<ArgList extends any[], B> extends Function { constructor(dispatch: TMultiMethod<ArgList, any>) { super(); const methods: Dictionary<TMultiMethod<ArgList, B>> = {}; const multi = function (...args: any[]): TMultiMethod<ArgList, B> { const dispatchKey = (dispatch as any)(...args); const method = methods[dispatchKey] || methods.default; if (!method) { throw new Error(`No method implementation for dispatch key ${dispatchKey}`); } return (method as any)(...args); }; multi.defmethod = (dispatchKey: any, method: TMultiMethod<ArgList, B>) => { if (methods[dispatchKey]) { console.warn(`Overriding definition of ${dispatchKey}`); } methods[dispatchKey] = method; }; return multi as any; } } export function defmulti<Input extends any | any[], Result>(dispatch: TMultiMethod<Input, any>) { return new MultiMethod<Input extends any[] ? Input : [Input], Result>(dispatch as any); } export function defmethod<M extends MultiMethod<any, any>>( multimethod: M, dispatchResult: any, impl: TMultiMethod<Parameters<M>, ReturnType<M>> ) { if (!(multimethod as any).defmethod) { throw new Error(`Can not extend given method: not a multi method`); } (multimethod as any).defmethod(dispatchResult, impl); }