Parameter Variance in Flow

Matthew Garcia

There are times when you may want a function to be able to take different types of parameters. A little bit like union types in TypeScript. Let’s see how it can be done using Flow.

Creating a Function That Takes a Parameter of Multiple Types

It’s just a matter of listing the possible types, delimited by a |:

function capitalize(
  // `capitalize` can take a string or an array of strings.
  words: string | Array<string>,
  // `capitalize` will return an array of strings either way.
): Array<string> {
  // At this point, Flow does not know if `words` is a string or an array,
  // so properties or methods specific to those types,
  // such as `map` and `toUpperCase`, cannot be accessed.
  // However, properties or methods that are shared by both, such as `length`, can be accessed.
  if (!Array.isArray(words)) {
    // Flow now knows that `words` is a string, so we can use string methods.
    return [words.toUpperCase()];
  }
  // Flow now knows that `words` is an array, so we can use array methods.
  return words.map(word => word.toUpperCase());
}

Helping Flow Determine the Type

The above example used Array.isArray to determine the type of words, but there are other ways Flow can do this:

// Through truthiness
function cube(val: number | void): number {
  // If `val` is falsy, it must be undefined (or 0).
  if (!val) {
    return 0;
  }
  // Otherwise, `val` must be a number.
  return val ** 3;
}

// With typeof
function getDigits(val: string | number): number {
  // If typeof val is 'number', it must be a number.
  if (typeof val === 'number') {
    return Math.ceil(Math.log10(val));
  }
  // Otherwise,`val` must be a string.
  return val.length;
}

// With instanceof
function getName(name: string | Nametag): string {
  // If `name` is an instance of the class `Nametag`, it must be a Nametag.
  if (name instanceof Nametag) {
    return name.name;
  }
  // Otherwise, `name` must be a string.
  return name;
}
  Tweet It
✖ Clear

πŸ•΅ Search Results

πŸ”Ž Searching...