Flow is a typed dialect of JavaScript.
It looks like TypeScript now. Plus component / renders / match. And still safer.
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');1type User = {2 readonly name: string,3 readonly age: number,4 readonly metadata: unknown,5};6function get<K extends keyof User>(7 user: User,8 key: K,9): User[K] {10 return user[key];11}12declare const user: User;13const age: number = get(user, 'age');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 Header1import * as React from 'react';2
3component Header(4 text: string = "Title",5) {6 return <h1>{text}</h1>;7}8component Footer() {return <footer />}9component Layout(10 header: renders Header,11) {12 return <div>{header}</div>;13}14
15<Layout header={<Header />} />; // OK16// Doesn't render Header:17<Layout header={<Footer />} />; // ERRORPattern 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 desc = match (action) { // ERROR: 'remove' missing7 {type: 'add', const text} => `Add: ${text}`,8 {type: 'toggle', const id} => `Toggle ${id}`,9};1type Action =2 | {type: 'add', text: string}3 | {type: 'toggle', id: string}4 | {type: 'remove', id: string};5declare const action: Action;6
7// 'remove' missing:8const desc = match (action) { // ERROR9 {type: 'add', const text} =>10 `Add: ${text}`,11 {type: 'toggle', const id} =>12 `Toggle ${id}`,13};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);1type Prices = {2 apple: number,3 banana: number,4};5const items = {6 apple: 1.5,7 banana: 0.5,8 sample: "free",9};10
11// Extra property `sample`:12const prices: Prices = items; // ERROR13
14Object.values(prices).map(price =>15 // `toFixed` on "free":16 `${price.toFixed(2)}` // Runtime crash!17);Safety by default
Pulling counter.incr off the instance detaches the method from counter, so the later tick() runs with this undefined and ++this.count throws. TypeScript types the extracted method as a plain function and waves the call through. Flow rejects the unbound method extraction at the point where this is lost. More safety differences from TypeScript
1class Counter {2 count: number = 0;3 incr(): number {4 return ++this.count;5 }6}7const counter = new Counter();8const tick = counter.incr; // ERROR: unsafe method extraction9tick(); // Runtime crash! Invalid `this` inside `incr`1class Counter {2 count: number = 0;3 incr(): number {4 return ++this.count;5 }6}7const counter = new Counter();8// Unsafe method extraction:9const tick = counter.incr; // ERROR10// Invalid `this` inside `incr`:11tick(); // Runtime crash!Use Flow in your project
Used in production at Meta across millions of files of JavaScript and React.