Reputation: 1355
I have the following:
const func1 = (state: Interface1){
//some code
}
const func2 = (state: Interface2){
//some other code
}
const func3: (state: Interface1|Interface2){
//some other code
}
but,
func3(func1)
gives me an error.
What I was trying to make was a function func3
that can take either func1
or func2
as a parameter. How do I do this in typescript?
Upvotes: 1
Views: 1494
Reputation: 33101
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
Upvotes: 2
Reputation: 4974
As you know, typescript is a strict language. So you should specify the exact parameter in func3. You can do something like this:
type Interface1={
//some code
}
type Interface2={
//some code
}
type Interface3 = ((state:Interface1)=>void) | ((state:Interface2)=>void)
const func1 = (state: Interface1)=>{
//some code
}
const func2 = (state: Interface2)=>{
//some other code
}
const func3= (state: Interface3)=>{
//some other code
}
//both of following func3 call is correct
func3(func1);
func3(func2);
Upvotes: 1