Skip to main content

Match Patterns

Match patterns both define a condition that must be matched, and new variables that are extracted (like destructuring).

Primitive value patterns

Primitive value patterns include string literals (e.g. 'light'), number literals (e.g. 42), BigInt literals (e.g. 10n), boolean literals (e.g. true), null, and undefined.

You can use variables (e.g. name) or property accesses (e.g. Status.Active) which are either typed as a literal type, or a Flow Enum member. Using variables that are a general type like string or number is not allowed for match patterns - they must be a literal type like 'light'. You can add a type annotation or as const to your string value to type it with a literal type. Computed properties in match patterns only allow literals like foo['bar'] or foo[2].

The identifier _ is special cased for “Wildcard patterns”, if you want to match against a value with that name, rename it to something else first. If you want to create a new variable, take a look at “variable declaration patterns” below - it is done by doing const x.

You can use a number literal prefixed with + or -, or BigInt literals prefixed with - (+ on a BigInt is an error in JS). +0 and -0 are not allowed (Flow doesn’t differentiate these type-wise from 0). NaN is special-cased, since NaN === NaN is always false. It is matched using Number.isNaN.

Other types of expressions are not supported. To match against an arbitrary expression (which has a literal type), first assign it to a variable, and then match against that variable.

Example:

1const Status = {active: 'active', paused: 'paused'} as const;2declare const ranks: [10, 20];3declare const fallbackLabel: 'unknown';4
5declare const value: unknown;6
7match (value) {8  0 => {}9  'archived' => {}10  null => {}11  -1 => {}12  fallbackLabel => {} // a literal-typed variable13  Status.active => {} // a property access14  Status['paused'] => {} // a computed string member15  ranks[0] => {} // a computed number member16  _ => {}17}

Flow will never output an “unused pattern” error for the undefined pattern, so you can always add it even if the input type does not contain void. This is to cover the case where Flow does not compute a type that is 100% accurate to the runtime value (for example, from indexed access of an array past its length).

At runtime, these checks are done using triple equals ===.

Wildcard patterns

Wildcard patterns, which are a single underscore _, match everything. If you want to match against the value of a variable named _, assign it to a different name first.

If part of your input type cannot be matched exhaustively (e.g. string), then the match will require a wildcard.

Example:

1declare const username: string;2
3match (username) {4  _ => {}5}

Variable declaration patterns

Variable declaration patterns, like const name, take whatever value is at that position and assign it to a new variable. Conditional check wise, they act like a wildcard and match everything.

Example:

1declare const input: string;2
3const greeting = match (input) {4  const name => `Hello, ${name}!`,5};

While let variables are also supported by the runtime, these are a type error for now and only const variables are allowed. If you have a use case, please share it with the team. var is not supported.

Object patterns

Object patterns match object values with the same structure. For example, the pattern {type: 'light', num: 42} matches objects which have the property type with value 'light', and a num property with value 42. If the object value has additional unlisted properties, or is inexact, you need to make your pattern inexact using ..., for example {type: 'light', num: 42, ...}.

Like destructuring, a variable declaration pattern nested inside an object pattern creates a new variable with the value of that property. E.g. match (arg) { {prop: const x} => x } will initialize x with the value arg.prop. You can use object rest to gather the rest of the object’s own properties: {foo: 1, ...const rest}.

Doing just {name} is ambiguous - it could mean {name: name} (matching against the value of variable name) or {name: const name} (extracting the property’s value as a new variable called name), so it’s not allowed. If you want a shorthand for creating new variables, you can use {const name} to mean {name: const name}.

If you have a property with either a wildcard or variable declaration pattern, e.g. {foo: _} or {foo: const x}, it is checked that the property is in the object (checks own and non-own properties).

When checking objects with optional properties, in order to make the check exhaustive you must include a pattern that doesn’t include the optional properties. For example, for {name: string, age?: number}, if you first match with the pattern {name: _, age: _}, you still need to match with the pattern {name: _, ...} in order to handle the cases where the age property doesn’t exist.

Property names can be identifiers (e.g. foo: pattern), string literals (e.g. 'foo': <pattern>), or number literals (e.g. 2: <pattern>). Repeated object keys are banned, and BigInts are not supported as object keys (Flow doesn’t yet support them).

Example:

1declare const event:2  | {kind: 'click', x: number, y: number}3  | {message: string}4  | {kind: 'scroll', delta: number};5
6const summary = match (event) {7  {kind: 'click', x: const x, y: const y} => `click at ${x},${y}`,8  {const message} => message,9  {kind: 'scroll', ...const rest} => `scroll ${rest.delta}`,10};

Getters are also not supported - at runtime they will be evaluated multiple times, once for each conditional check done against them.

Instance patterns

Instance patterns match instances of a class. The pattern is a class name followed by an object pattern, for example Point {x: 1, ...} matches values that are instances of Point whose x property is 1. At runtime this is like checking p instanceof Point && p.x === 1.

The object pattern part works just like a regular object pattern: you can extract properties with variable declaration patterns, use the {const x} shorthand, and gather the rest with object rest.

Instances are always inexact, since a class can be extended with additional properties, so you must end the pattern with ....

Example:

1class Point {2  x: number;3  y: number;4}5declare const p: Point;6
7const sum = match (p) {8  Point {const x, const y, ...} => x + y,9};

If you leave off the ..., Flow errors, because the instance could have additional properties:

1class Point {2  x: number;3  y: number;4}5declare const p: Point;6
7match (p) {8  Point {const x, const y} => {} // ERROR: instances are inexact, so `...` is requiredmatch-not-exhaustiveThis instance pattern hasn't considered all possible properties of the input. There could be additional properties due to Point [1]. To fix, add ... to the end of the pattern to match all other properties.9}

Different classes in a union can each be matched by their own pattern, and the body is refined to that class:

1class Shape {2  area(): number {3    return 0;4  }5}6class Circle extends Shape {7  radius: number;8}9class Square extends Shape {10  side: number;11}12
13declare const shape: Circle | Square;14
15const size = match (shape) {16  Circle {const radius, ...} => radius, // matches `Circle` instances17  Square {const side, ...} => side, // matches `Square` instances18};

Matching follows the prototype chain like instanceof: a pattern naming a superclass matches instances of any subclass.

1class Shape {2  area(): number {3    return 0;4  }5}6class Circle extends Shape {7  radius: number;8}9class Square extends Shape {10  side: number;11}12
13declare const shape: Circle | Square;14
15const out = match (shape) {16  Shape {...} => shape.area(), // a superclass pattern matches any subclass instance17};

Object patterns are structural and also match instances, so {const x, const y, ...} matches a Point. An instance pattern, on the other hand, only matches instances of the named class, and never a plain object.

1class Point {2  x: number;3  y: number;4}5declare const p: Point;6
7const sum = match (p) {8  {const x, const y, ...} => x + y, // an object pattern matches the instance9};

The constructor must reference a single class. Using a type, interface, or other value is an error.

1class Point {2  x: number;3  y: number;4}5
6declare const p: Point;7declare const NotAClass: string;8
9match (p) {10  NotAClass {...} => {} // ERROR: constructor must reference a single classmatch-invalid-patternInvalid match instance pattern constructor. It must reference a single class.11  _ => {}12}

Instance patterns are available by default since Flow v0.317.

Array patterns

Array patterns match both tuple and array values. For example, the pattern ['small', true] matches array values whose length is 2 and whose elements match the pattern’s elements. If you want a looser length check, you can make the pattern inexact using ..., for example ['small', true, ...] will match arrays whose length is >= 2 and whose first elements match the pattern’s elements. You can match any array or tuple with [...].

Like destructuring, a variable declaration pattern nested inside an array pattern creates a new variable with the value of that element. E.g. match (arg) { [const x] => x } will initialize x with the value arg[0]. You can use array rest at the end of the pattern to gather the remaining elements, and check that the length is greater or equal to the pattern length: e.g. [1, 2, ...const rest].

Array patterns match values which pass Array.isArray, so they won’t match array-like objects that aren’t actually arrays, and won’t match iterables. Use Array.from on those types of values first if you want to match them with array patterns.

Example:

1declare const color: [0, 0, 0] | [255, 0, 0, 1] | [0, 0, 255, 0.5];2
3const css = match (color) {4  [0, 0, 0] => 'black',5  [255, 0, 0, ...] => 'opaque red',6  [0, 0, 255, ...const rest] => `blue at ${rest[0]} alpha`,7};

“Or” patterns

Or patterns allow you to combine multiple patterns using |, for example 'active' | 'paused' will match either string literal.

Variable declaration patterns inside of "or" patterns are not yet supported.

Example:

1declare const reply: 'yes' | 'yeah' | 'no' | 'nope';2
3const answer = match (reply) {4  'yes' | 'yeah' => true,5  'no' | 'nope' => false,6};

“As” patterns

As patterns both match a pattern and create a new variable. For example, [_, _] as pair will first match any arrays whose length is 2, and then assign that value to a new variable called pair.

Example:

1declare const result: ['ok', number] | ['error', string];2
3const handled = match (result) {4  ['ok', _] as success => success,5  ['error', _] as failure => failure,6};

See Also

  • Tuples — tuple patterns in match expressions
  • Objects — object patterns in match expressions
  • Classes — instance patterns in match expressions