3
votes

I'm working on adding flow types to a JS codebase and have run into some untyped code like this:

const doSomething = (callback, type) => {
    if (type === 'one') {
        const bool = callback(42)
    } else if (type === 'two') {
        const str = callback(4, 2)
    }
}

The function takes a callback that has one of two signatures, and a type argument which is a string that indicates which signature the callback uses.

In my first attempt to add flow, I've ended up with the following:

/* @flow */

type Callback1 = (any) => bool
type Callback2 = (any, any) => string

type WhichFunc = 'one' | 'two'
type Func = Callback1 | Callback2

const test = (func: Func, which: WhichFunc) => {
  if (which === 'one') {
    const b: bool = func(42)
  } else if (which === 'two') {
    const s: string = func(4, 2)
  }
}

This fails, unsurprisingly, because flow has no way to verify that the which string has any relation to func.

Is there a way to add flow types here without changing the API?

1
I am confused by the original doSomething function. It doesn't compile for me (missing = ?), and it appears to have no return value and no side effects. What is it intended to do? - dcorking
It was a simplified example (yes, I was missing the =). The point is it does something and then invokes a callback, but the signature of the callback depends on the type argument. - zmb

1 Answers

1
votes

You can make an explicit (but cumbersome) typecast like this:

const test = (func: Func, which: WhichFunc) => {
  if (which === 'one') {
    const callback: Callback1 = ((func: any): Callback1);
    const b: bool = callback(42);
  } else if (which === 'two') {
    const callback: Callback2 = ((func: any): Callback2);
    const s: string = callback(4, 2)
  }
};

You can also see this on flow.org/try 🙂