Skip to main content

Flow is a typed dialect of JavaScript.

It looks like TypeScript now. Plus component / renders / match. And still safer.

01 — Familiar

Familiar syntax

Is it Flow or TypeScript? You can't tell. Flow has keyof, readonly props, unknown, indexed access types T[K], and extends generic bounds. Plus conditional types, mapped types, and type guards. More TS-compatible syntax

1type User = {2  readonly name: string,3  readonly age: number,4  readonly metadata: unknown,5};6function get<K extends keyof User>(user: User, key: K): User[K] {7  return user[key];8}9declare const user: User;10const age: number = get(user, 'age');
02 — React

First-class React

component and renders are built into Flow's syntax. Props are named parameters, typed inline with defaults, so there's no props object to annotate or destructure. Only components that render Header can flow into a renders Header prop: design-system composition rules become type errors instead of bugs caught in review. More on component and renders

1import * as React from 'react';2
3component Header(text: string = "Title") { return <h1>{text}</h1> }4component Footer() { return <footer /> }5
6component Layout(header: renders Header) {7  return <div>{header}</div>;8}9
10<Layout header={<Header />} />; // OK11<Layout header={<Footer />} />; // ERROR: doesn't render Header
Cannot create Layout element because in property header: Footer element [1] does not render Header [2].
03 — Match

Pattern matching with match

match is both an expression (shown here) and a statement: a safer switch with no fall-through. It's exhaustively checked, with structural patterns that destructure as they match. Forget a case, like 'remove', and match flags it and tells you what to add. More on match

1type Action = {type: 'add', text: string}2            | {type: 'toggle', id: string}3            | {type: 'remove', id: string};4declare const action: Action;5
6const description = match (action) { // ERROR: 'remove' missing7  {type: 'add', const text} => `Add: ${text}`,8  {type: 'toggle', const id} => `Toggle ${id}`,9};
match hasn't checked all possible cases of the input type. To fix, add the missing pattern: {type: 'remove', id: _} to match object type [1].
04 — Objects

Exact objects by default

Object types in Flow are exact by default. TypeScript only catches extras on direct object-literal assignment, so sample: "free" slips through once the object goes through a variable. Flow catches it at the assignment, before the crash. More on object exactness

1type Prices = {apple: number, banana: number};2const items = {apple: 1.5, banana: 0.5, sample: "free"};3
4const prices: Prices = items; // ERROR: extra property `sample`5
6Object.values(prices).map(price =>7  `${price.toFixed(2)}`, // Runtime crash! `toFixed` on "free"8);
Cannot assign items to prices because property sample is extra in object literal [1] but missing in Prices [2]. Exact objects do not accept extra props.
05 — Safety

Safety by default

Extracting a method from a class instance loses its this binding. TypeScript lets the extraction through; tick() then crashes inside increment because this is undefined. Flow rejects the extraction. More differences from TypeScript

1class Counter {2  count: number = 0;3  increment(): number {4    return ++this.count;5  }6}7const counter = new Counter();8const tick = counter.increment; // ERROR: unsafe method extraction9tick(); // Runtime crash! Invalid `this` inside `increment`
Cannot get counter.increment because property increment [1] cannot be unbound from the context [2] where it was defined.

Use Flow in your project

Used in production at Meta across millions of files of JavaScript and React.