func3 expects Interface1 | Interface2 when you want to pass func1.
func1 has completely different type signature:
(state: Interface1) => void
If you want to pass a callback, you need to change func3 argument type:
type Interface1 = {
tag: 'Interface1'
}
type Interface2 = {
tag: 'Interface2'
}
const func1 = (state: Interface1) => {
//some code
}
const func2 = (state: Interface2) => {
//some other code
}
const func3 = (cb: typeof func1 | typeof func2) => {
//some other code
}
func3(func1) // ok
If you want to compose functions this article might be interesting for you
UPDATE
Previous solutions works, but it is impossible to call callback. My bad, sorry for that.
In order to do that, you have to refactor your code a bit.
I thought that simple union type should help, but we have a deal with functions.
It is not that easy.
Consider next example:
type Union = typeof func1 | typeof func2
const func3 = (cb: Union) => {
cb({ tag: 'Interface2' }) // error
}
func3(func1) // ok
In above case cb is infered to (arg:never)=>any. Why ?
Please take a loot at @jcalz great answer regarding intersection.
The main point is - that types in contravariant positions get intersected.
And because you can't create Interface1 & Interface2 it resolved to never.
If you are wonder what is contravariance, variance etc ... please take a loot at my question.
So, TS does not know which argument is allowed and which is not.
As you see in the example, I have passed func1 as an argument but tried to call callback with Interface2 - it can throw an error in runtime.
You can handle it in this way:
type Interface1 = {
tag: 'Interface1'
}
type Interface2 = {
tag: 'Interface2'
}
const func1 = (state: Interface1) => {
//some code
}
const func2 = (state: Interface2) => {
//some other code
}
type Fn = (...args: any[]) => any
function func3<Cb extends Fn, Param extends Parameters<Fn>>(cb: Cb, ...args: Param) {
cb(args)
}
const x = func3(func1, { tag: 'Interface1' }) // ok
Function arguments are contravariant to each other if you want to create union of functions that why it throws an error
UPDATE 3
Ok, ok, this may be not what you expect.
If you still want to compute func1 argument inside func3, you need to use typeguard
type Interface1 = {
tag: 'Interface1'
}
type Interface2 = {
tag: 'Interface2'
}
const func1 = (state: Interface1) => {
//some code
}
const func2 = (state: Interface2) => {
//some other code
}
type Fn = (a: any) => any
// typeguard
const isFunc = <R extends typeof func1 | typeof func2>(cb: Fn, cb2: R): cb is R => cb === cb2
const func3 = (cb: typeof func1 | typeof func2) => {
if (isFunc(cb, func1)) {
cb({ tag: 'Interface1' })
} else {
cb({ tag: 'Interface2' })
}
}
func3(func1) // ok