I have a function that takes a generic input and callback and passes it to another callback that manages the execution and adds a separate generic input. It works well as long as the handler callback takes only 1 generic prop as a parameter:
interface Output <X> { time: Date, value: X }
interface Identity { id: string | number }
interface Input <X> extends Identity { x: X }
type Handler <X> = (props: { time: Date, value: Input<X> }) => Output<X>
function handle <X> (props: {
input: Input<X>
handler: Handler<X>
manager: (props: {
value: Input<X>
handler: Handler<X>
}) => Output<X>
}): Output<X> {
console.log('Handling:', props.input.id)
return props.manager({ value: props.input, handler: props.handler })
}
function timer <X> (props: { time: Date, value: Input<X> }): Output<X> {
return { time: props.time, value: props.value.x }
}
const time = new Date()
const stringInput: Input<string> = { id: 'hello', x: 'world' }
const stringOutput = handle({
input: stringInput,
handler: timer,
manager: (props) => props.handler({ time, value: props.value })
})
function stringX (output: Output<string>): void {
console.log('String:', output.value)
}
stringX(stringOutput)
const numberOutput = handle({
input: { id: 'answer', x: 42 },
handler: timer,
manager: (props) => props.handler({ time, value: props.value })
})
function numberX (output: Output<number>): void {
console.log('Number + 1:', output.value + 1)
}
numberX(numberOutput)
Playground: https://tsplay.dev/NlKBBw
However, if the handler takes two generic props, the handler’s argument is not inferred:
interface Timing <V> { date: Date, value: V }
function timer <V> (props: { time: Timing<V>, input: Input<V> }): Output<V> {
return { time: props.time.date, value: props.input.value }
}
const stringInput = { id: 'hello', value: 'world', a: 'goodbye' }
const stringTime = { date: new Date(), value: 'test' }
const stringOutput = handle({
input: stringInput,
handler: timer,
manager: (props) => props.handler({ time: stringTime, input: props.input }) // Argument of type '{ time: { date: Date; value: string; }; input: Input<string>; }' is not assignable to parameter of type 'never'. ts(2345)
})
function stringValue (output: Output<string>): void {
console.log('String:', output.value)
}
stringValue(stringOutput)
const numberInput: Input<number> = { id: 'answer', value: 42, a: 'goodbye' }
const numberTime: Timing<number> = { date: new Date(), value: 84 }
const numberOutput = handle({
input: numberInput,
handler: timer,
manager: (props) => props.handler({ time: numberTime, input: props.input }) // Argument of type '{ time: Timing<number>; input: Input<number>; }' is not assignable to parameter of type 'never'.ts(2345)
})
function numberValue (output: Output<number>): void {
console.log('Number + 1:', output.value + 1)
}
numberValue(numberOutput)
Playground: https://tsplay.dev/W4gzAm
If I give explicit generics to the handler callback before I pass it, it works again:
interface Timing <V> { date: Date, value: V }
function timer <V> (props: { time: Timing<V>, input: Input<V> }): Output<V> {
return { time: props.time.date, value: props.input.value }
}
const stringInput = { id: 'hello', value: 'world', a: 'goodbye' }
const stringTime = { date: new Date(), value: 'test' }
const stringTimer: Handler<string, { time: Timing<string> }> = timer
const stringOutput = handle({
input: stringInput,
handler: stringTimer,
manager: (props) => props.handler({ time: stringTime, input: props.input }) // No error
})
function stringValue (output: Output<string>): void {
console.log('String:', output.value)
}
stringValue(stringOutput)
const numberInput = { id: 'answer', value: 42, a: 'goodbye' }
const numberTime = { date: new Date(), value: 84 }
const numberTimer: Handler<number, { time: Timing<number> }> = timer
const numberOutput = handle({
input: numberInput,
handler: numberTimer,
manager: (props) => props.handler({ time: numberTime, input: props.input }) // No error
})
function numberValue (output: Output<number>): void {
console.log('Number + 1:', output.value + 1)
}
numberValue(numberOutput)
Playground: https://tsplay.dev/WyLnJm
How can I infer the handler props when both are generic?