Migrating from legacy patterns

Learn how to migrate to Flow Enums from legacy JavaScript enum patterns like Object.freeze.

First, learn how to update the enum definition site, and next learn how to update files that import and use the enum.

Updating definitions

Object.freeze

If you are using Object.freeze, you can migrate to an enum if the values of the object are:

  • All the same primitive type, and that type is boolean, string, number, or symbol.
  • All literals.
  • Contain no duplicate values.

Replace

const Status = Object.freeze({
  Active: 1,
  Paused: 2,
  Off: 3,
});

export type StatusType = $Values<typeof Status>;

export default Status;

with

export default enum Status {
  Active = 1,
  Paused = 2,
  Off = 3,
}
  • Check to ensure that the key names do not start with lowercase ‘a’-‘z’ (disallowed in enums). If they do, you’ll need to rename the member names.
  • Remove any usage of $Keys<...> or $Values<...> on the enum type, these are no longer needed as a Flow Enum defines a type itself (its name).
  • Delete any type exports based on the enum, as you just need to export the Flow Enum. A Flow Enum acts as both a type and a value (like a class).

Then, take a look at how to update files that import and use the enum.

keyMirror

The keyMirror utility creates an object whose values are mirrors of its key names. You can replace keyMirror usage with a string based enum.

Replace

import keyMirror from 'keyMirror';

const Status = keyMirror({
  Active: null,
  Paused: null,
  Off: null,
});

export type StatusType = $Keys<typeof Status>;

export default Status;

with

export default enum Status {
  Active,
  Paused,
  Off,
}
  • Check to ensure that the key names do not start with lowercase ‘a’-‘z’ (disallowed in enums). If they do, you’ll need to rename the member names.
  • Remove any usage of $Keys<...> on the enum type, it’s no longer needed as a Flow Enum defines a type itself (its name).
  • Delete any type exports based on the enum, you just need to export the Flow Enum. A Flow Enum acts as both a type and a value (like a class).

Then, take a look at how to update files that import and use the enum.

Updating usage

Fix type imports

Previous patterns required you to export (and then import) a type separate from the enum itself. Flow Enums are both types and values (like a class), so you just need to export the Flow Enum itself. Since there is now one export, you only need one import. Read more about exporting enums and importing enums.

If you previously had:

const Status = Object.freeze({
  Active: 1,
  Paused: 2,
  Off: 3,
});
export type StatusType = $Values<typeof Status>;
export default Status;

And you’ve replaced it with:

export default enum Status {
  Active = 1,
  Paused = 2,
  Off = 3,
}

Then you need to fix the imports as well:

If both type and value were imported

For a user of the enum, if you previously imported both the type and the value, you can delete the type import and update annotations used.

Change

import type {StatusType} from 'status';

import Status from 'status';

const myStatus: StatusType = Status.Active;

to

// Type import is deleted

import Status from 'status';

const myStatus: Status = Status.Active; // Changed type annotation to just `Status`

If only the type was imported

For a user of the enum, if you previously imported just the type, change the type import to a default import rather than a named import.

Change

import type {StatusType} from 'status';

function isActive(status: StatusType) { ... }

to

// Remove the braces `{` `}` and changed the name - this is a default import now
import type Status from 'status';

function isActive(status: Status) { ... } // Changed type annotation to just `Status`

Mapping enums to other values

Sometimes you want to map from an enum value to some other value. Previously, we sometimes used object literals for this. With Flow Enums, use a function with a switch instead. The switch is exhaustively checked, so Flow will ensure you update your mapping when you add or remove Flow Enum members.

Replace this pattern

const STATUS_ICON: {[Status]: string} = {
  [Status.Active]: 'green-checkmark',
  [Status.Paused]: 'grey-pause',
  [Status.Off]: 'red-x',
};
const icon = STATUS_ICON[status];

with

function statusIcon(status: Status): string {
  switch (status) {
    case Status.Active:
      return 'green-checkmark';
    case Status.Paused:
      return 'grey-pause';
    case Status.Off:
      return 'red-x';
  }
}
const icon = statusIcon(status);

Read more about mapping enums to other values.

Usage as the representation type (e.g. a string)

You can’t use a Flow Enum directly as its representation type (e.g. a string). If you get Flow errors about using an enum as its representation type, first try to refactor your code so that it expects the enum type instead of the representation type (e.g. change annotations from string to Status). If you really want to use the enum as its representation type, you can add in explicit casts. See casting to represetation type.

Casting to the enum type

If before you cast from an enum’s representation type (e.g. string) to the enum type with something like this:

function castToStatus(input: number): StatusType | void {
  switch(input) {
    case 1: return Status.Active;
    case 2: return Status.Paused;
    case 3: return Status.Off;
    default: return undefined;
  }
}

castToStatus(x);

You can now just use the cast method:

Status.cast(x);

Update switch statements

Flow Enums are exhaustively checked in switch statements. You might need to update your code when you are switching over an enum value. Read more at exhaustively checking enums in switch statements.

Operations over enum members

If previously you used functionality like Object.values, Object.keys, or for-in loops to get and operate on the enum members, you can use the members method instead.

© 2013–present Facebook Inc.
Licensed under the MIT License.
https://flow.org/en/docs/enums/migrating-legacy-patterns