pragmatic-fp-ts
Version:
Opinionated functional programming library with easy use in mind
47 lines (40 loc) • 1.62 kB
text/typescript
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);
}