// define callbacks
const checkValue = (key: string, value: unknown) => {
if (typeof value !== 'number' || Number.isNaN(value)) throw new Error('error ' + key)
return value
}
const checkRange = (key: string, value: unknown, min: number, max: number = Infinity) => {
if (typeof value !== 'number' || Number.isNaN(value)) throw new Error('error ' + key)
if (value < min || value > max) throw new Error('error')
return value
}
const checkSomething = (key: string, value: unknown, something: number) => {
if (typeof value !== 'number' || Number.isNaN(value)) throw new Error('error ' + key)
if (value === something) throw new Error('error')
return value
}
type Object = { [P: string]: unknown }
const testObject: Record<string, unknown> = { one: 1, two: 'two' }
// main
const evaluate = <
O extends Object,
F extends (key: string, val: unknown) => ReturnType<F>
>(obj: O, prop: keyof O, vfunc: F) => {
if (typeof prop !== 'string') throw new Error('error')
return vfunc(prop, obj[prop])
}
const evaluateTwo = <
O extends Object,
F extends (key: string, val: unknown, ...args: unknown[]) => ReturnType<F>
>(obj: O, prop: keyof O, vfunc: F , ...args: unknown[]) => {
if (typeof prop !== 'string') throw new Error('error')
return vfunc(prop, obj[prop], ...args)
}
evaluate(testObject, 'one', checkValue) // good
evaluate(testObject, 'one', checkRange) // error as expected
// the following expression statements should not fail
evaluateTwo(testObject, 'one', checkRange) // should say missing `min` arg
evaluateTwo(testObject, 'one', checkRange, 1)
evaluateTwo(testObject, 'one', checkRange, 1, 10)
evaluateTwo(testObject, 'one', checkSomething, 1) // should say wrong type number instead of string
In the snippet above, I have defined callback functions checkValue
, checkRange
, and checkSomething
, and showcased the usage of evaluate
and evaluateTwo
functions for different scenarios. The code also includes error messages and playground links for further reference.