Skip to main content

Defining enums

Learn how to define a Flow Enum. Looking for a quick overview? Check out the Quickstart Guide.

An enum declaration is a statement. Its name defines both a value (from which to access its members, and call its methods), and a type (which can be used as an annotation for the type of its members).

Enum members must all be of the same type, and those members can be one of four types: string, number, boolean, and symbol.

Every enum has some common properties:

Consistent member type

The type of the enum members must be consistent. For example, you can’t mix string and number members in one enum. They must all be strings, numbers, or booleans (you do not provide values for symbol based enums).

Member name starting character

Member names must be valid identifiers (e.g. not start with numbers), and must not start with lowercase a through z. Names starting with those letters are reserved for enum methods (e.g. Status.cast(...)).

This is not allowed:

1enum Status {2  active, // Error: names can't start with lowercase 'a' through 'z'
3}
2:3-2:8: Enum member names cannot start with lowercase 'a' through 'z'. Instead of using `active`, consider using `Active`, in enum `Status`.

Unique member names

Member names must be unique. This is not allowed:

1enum Status {2  Active,3  Active, // Error: the name 'Active` was already used above
4}
3:3-3:8: Enum member names need to be unique, but the name `Active` has already been used before in enum `Status`.

Literal member values

If you specify a value for an enum member, it must be a literal (string, number, or boolean), not a computed value. This is not allowed:

1enum Status {2  Active = 1 + 2, // Error: the value must be a literal
3}
2:12-2:12: The enum member initializer for `Active` needs to be a literal (either a boolean, number, or string) in enum `Status`.

Unique member values

Member values must be unique. This is not allowed:

1enum Status {2  Active = 1,3  Paused = 1, // Error: the value has already been used above
4}
3:12-3:12: Invalid enum member initializer. Initializers need to be unique, but this one has already been used for a previous member [1] of enum `Status` [2]. [duplicate-enum-init]

Fixed at declaration

An enum is not extendable, so you can’t add new members after the fact while your code is running. At runtime, enum member values can’t change and the members can’t be deleted. In this way they act like a frozen object.

String enums

String enums are the default. If you don’t specify an of clause (e.g. enum Status of number {}, enum Status of symbol {}, etc.), and do not specify any values (e.g. enum Status {Active = 1}) then the definition will default to be a string enum.

Unlike the other types of enums (e.g. number enums), you can either specify values for the enum members, or not specify values and allow them to be defaulted.

If you don’t specify values for your enum members, they default to strings which are the same as the name of your members.

1enum Status {2  Active,3  Paused,4  Off,5}

Is the same as:

1enum Status {2  Active = 'Active',3  Paused = 'Paused',4  Off = 'Off',5}

You must consistently either specify the value for all members, or none of the members. This is not allowed:

1enum Status {2  Active = 'active',3  Paused = 'paused',4  Off, // Error: you must specify a value for all members (or none of the members)
5}
4:3-4:5: String enum members need to consistently either all use initializers, or use no initializers, in enum Status.

Optionally, you can use an of clause:

1enum Status of string {2  Active,3  Paused,4  Off,5}

We infer the type of the enum based on its values if there is no of clause. Using an of clause will ensure that if you use incorrect values, the error message will always interpret it as an enum of that type.

Number enums

Number enums must have their values specified.

You can specify a number enum like this:

1enum Status {2  Active = 1,3  Paused = 2,4  Off = 3,5}

Optionally, you can use an of clause. This does not affect the type-checking behavior of a valid Flow Enum, it just ensures that all enum members are numbers as the definition site.

1enum Status of number {2  Active = 1,3  Paused = 2,4  Off = 3,5}

We do not allow defaulting of number enums (unlike some other languages), because if a member from the middle of such an enum is added or removed, all subsequent member values would be changed. This can be unsafe (e.g. push safety, serialization, logging). Requiring the user to be explicit about the renumbering makes them think about the consequences of doing so.

The value provided must be a number literal. (Note: there is no literal for negative numbers in JavaScript, they are the application of a unary - operation on a number literal.) We could expand allowed values in the future to include certain non-literals, if requests to do so arise.

Boolean enums

Boolean enums must have their values specified. Boolean enums can only have two members.

You can specify a boolean enum like this:

1enum Status {2  Active = true,3  Off = false,4}

Optionally, you can use an of clause. This does not affect the type-checking behavior of a valid Flow Enum, it just ensures that all enum members are booleans as the definition site.

1enum Status of boolean {2  Active = true,3  Off = false,4}

Symbol enums

Symbol enums can’t have their values specified. Each member is a new symbol, with the symbol description set to the name of the member. You must use the of clause with symbol enums, to distinguish them from string enums, which are the default when omitting values.

You can specify a symbol enum like this:

1enum Status of symbol {2  Active,3  Paused,4  Off,5}

Flow Enums with Unknown Members

You can specify that your enum contains "unknown members" by adding a ... to the end of the declaration:

1enum Status {2  Active,3  Paused,4  Off,5  ...6}7const status: Status = Status.Active;8
9switch (status) {
10 case Status.Active: break;11 case Status.Paused: break;12 case Status.Off: break;13}
9:9-9:14: Missing `default` case in the check of `status`. `Status` [1] has unknown members (specified using `...`) so checking it requires the use of a `default` case to cover those members. [invalid-exhaustive-check]

When this is used, Flow will always require a default when switching over the enum, even if all known enum members are checked. The default checks for "unknown" members you haven't explicitly listed.

This feature is useful when an enum value crosses some boundary and the enum declaration on each side may have different memebers. For example, an enum definition which is used on both the client and the server: an enum member could be added, which would be immediately seen by the server, but could be sent to an outdated client which isn't yet aware of the new member.

One use case for this would be the JS output of GraphQL Enums: Flow Enums with unknown members could be used instead of the added '%future added value' member.

Enums at runtime

Enums exist as values at runtime. We use a Babel transform to transform Flow Enum declarations into calls to the enums runtime (read more in the enabling enums documentation). We use a runtime so all enums can share an implementation of the enum methods.

We use Object.create(null) for enums' prototype (which has the enum methods), so properties in Object.prototype will not pollute enums. The only own properties of the enum object are the enum members. The members are non-enumerable (use the .members() method for that). The entire enum object is frozen, so it cannot be modified.

Style guide

Naming enums

We encourage you to define enum names in PascalCase, following the naming conventions of other types. All caps names (e.g. STATUS) are harder to read and discouraged.

We encourage you to name enums in the singular. E.g. Status, not Statuses. Just like the type of true and false is boolean, not booleans.

Don't append Enum to the name (e.g. don't name your enum StatusEnum). This is unnecessary, just like we don't append Class to every class name, and Type to every type alias.

Naming enum members

We encourage you to define enum member names in PascalCase. All caps names (e.g. ACTIVE) are harder to read and discouraged. Additionally, since Flow enforces that these are constants, you don't need to use the name to signal that intent to the programmer.

See also: the rule about enum member name starting characters.

Don't create a separate type

A Flow Enum, like a class, is both a type and a value. You don't need to create a separate type alias, you can use the enum name.

Use dot access for accessing members

Prefer Status.Active vs. const {Active} = Status;. This makes it easier find uses of the enum with text search, and makes it clearer to the reader what enum is involved. Additionally, this is required for switch statements involving enums.