typomath/src/util/overload.ts

43 lines
1.6 KiB
TypeScript

import {reflect, CallSite} from 'typescript-rtti'
type UnionToIntersection<U> =
(U extends any ? (k: U) => void : never) extends ((k: infer I) => void)
? I
: never;
export default function overload<T extends readonly [...any[]]>(
imps: T, callSite?: CallSite): UnionToIntersection<T[number]> {
const impTypes = reflect(callSite).parameters[0].elements
return <any>((...a: any[]) => {
for (let i = 0; i < imps.length; ++i) {
const paramTypes = impTypes[i].type.parameters
let match = true
const haveArgs = a.length
let onArg = 0
for (const param of paramTypes) {
if (param.isRest) {
// All the rest of the arguments must be of param's type
match = a.slice(onArg).every(
arg => param.type.matchesValue(arg))
break
}
if (onArg === haveArgs) {
// We've used all of the arguments, so better be optional
match = param.isOptional
break
}
// This argument must match this param's type and both are used
match = param.type.matchesValue(a[onArg])
onArg += 1
if (!match) break
}
// Make sure we used all the arguments
match &&= (onArg == haveArgs)
if (match) return imps[i](...a)
}
throw new TypeError(
`Actual arguments ${a} of type ${a.map(arg => typeof arg)} `
+ 'did not match any implementation')
})
}