Flow Static Type Checker
repository·main·Indexed 12 days ago
https://github.com/facebook/flowA static typechecker for JavaScript designed to help developers catch type-related errors during development. This documentation includes details on the Flow AI Evals suite, which provides LLM coding evaluations for the type checker, including TypeScript to Flow conversion, project patterns with Relay integration, and various grading mechanisms using AST and flow-check.
What's inside Flow
- Flow is a static typechecker for JavaScript. It allows developers to add type annotations to JavaScript code to catch errors during development. For detailed documentation and getting started guides, visit flow.org.
Overview of try-flow-website-js
mainThe
try-flow-website-jspackage is a specialized NPM package that contains compiledflow.jsfiles andlibdefsfor every version of Flow.Note: This package is intended for consumption exclusively by the flow.org/try playground. It is not intended for general use in standard application development workflows.
What is flow-parser
mainTheflow-parseris a JavaScript parser built from the Flow parser compiled to WebAssembly. It is capable of parsing ES6, Flow, and JSX syntax. It produces an Abstract Syntax Tree (AST) that conforms to the ESTree spec.Use Flow types for the Flow-ESTree spec
mainTheflow-estreepackage provides Flow type definitions for the Flow-ESTree specification. This specification defines the structure of the Abstract Syntax Tree (AST) produced by the Flow parser, ensuring that tools consuming Flow-generated ASTs can have type safety when working with the spec.Compare Flow and TypeScript syntax and semantics
mainFlow and TypeScript share significant overlap in syntax and vocabulary, including conditional types, mapped types, type guards,
keyof,as const,unknown, andReadonly. While Flow's syntax has aligned closely with TypeScript over time, Flow often makes deliberate choices to provide stronger static guarantees, rejecting certain patterns that TypeScript allows but which might cause runtime errors or logic bugs.Key Differences:
- React Integration: Flow features first-class syntax for React, specifically
component,hook, andrenders. - Tooling Scope: Unlike
tsc, theflowbinary is a typechecker only and does not emit JavaScript. You must use separate build tools to compile Flow syntax to runtime JS.
Note: TypeScript claims in this documentation are verified against version 6.0.3 with
strictenabled.type User = { readonly name: string, readonly age: number, readonly metadata: unknown, }; function get<K extends keyof User>( user: User, key: K, ): User[K] { return user[key]; } declare const user: User; const age: number = get(user, 'age');- React Integration: Flow features first-class syntax for React, specifically
What is the Hermes ESLint Scope Manager?
mainThe Hermes ESLint Scope Manager is a specialized scope manager designed to support Flow. It is a fork of thetypescript-eslintscope manager, enhanced specifically to handle the nuances of Flow's type system and syntax within an ESLint environment.What is a Library Definition (libdef)?
mainA Library Definition (or "libdef") is a special file used to inform Flow about the type signatures of third-party modules or packages that do not have built-in type information or have inaccurate types.
Think of libdefs as similar to header files in C++. They allow you to provide type safety for external dependencies so that Flow doesn't treat their exports as
any.Flow syntax and TypeScript compatibility
mainFlow provides a syntax highly compatible with TypeScript, supporting advanced type constructs such as
keyof,readonlyproperties,unknowntypes, indexed access typesT[K], andextendsgeneric bounds. It also includes conditional types, mapped types, and type guards.type User = { readonly name: string, readonly age: number, readonly metadata: unknown, }; function get<K extends keyof User>(user: User, key: K): User[K] { return user[key]; } declare const user: User; const age: number = get(user, 'age');Handle Array invariance with ReadonlyArray
mainMutable arrays (
Array<T>) are invariantly typed. This means you cannot pass anArray<string>to a function expectingArray<string | number>because the function could mutate the array by pushing anumber, which would invalidate the originalArray<string>type.Solution: Change the function argument type to
ReadonlyArray<T>. This makes the array covariant, preventing mutations and allowing narrower types to be passed.const fn = (arr: ReadonlyArray<string | number>) => { // arr.push(321); // This would now be a compiler error }; const arr: Array<string> = ['abc']; fn(arr);Create Polymorphic Component Types
mainPolymorphic Component Types are useful for 'transparent' components that preserve the type of their children or other passed-in elements. This is achieved using generic type parameters (e.g.,
<T extends React.Node>) in the component type signature.import * as React from 'react'; declare const TransparentComponent: component<T extends React.Node>(children: T) renders T; component Example() { return null } const element: renders Example = ( <TransparentComponent> <Example /> </TransparentComponent> );Compare Tuples and Arrays
mainIn Flow, tuples and arrays are distinct types and are not interchangeable in most cases:
- Array to Tuple: An
Array<T>cannot be passed into a tuple because Flow cannot guarantee the array's length. - Tuple to Array: A tuple cannot be passed into an
Array<T>because it would allow unsafe mutations like.push(). - Tuple to ReadonlyArray: You can pass a tuple into a
ReadonlyArray<T>type because mutation is disallowed.
TypeScript Comparison Note:
- Flow does not support spreading a tuple with optional elements in a way that TS does (Flow may error with
invalid tuple arity). - Flow requires labeled forms for optional unlabeled elements (e.g.,
[a: number, b?: string]), whereas TS allows[number, string?]. - Flow does not support the
readonly [T, S]shorthand; useReadonly<[T, S]>instead.
const array: Array<number> = [1, 2]; const tuple: [number, number] = array; // Error! const tuple2: [number, number] = [1, 2]; const readonlyArray: ReadonlyArray<number> = tuple2; // Works!- Array to Tuple: An
Handle `this` binding in classes and object literals
mainFlow enforces strict rules to prevent runtime crashes caused by
thisbeing undefined when a method is extracted or called.1. Method extraction from Classes
Flow bans extracting methods from class instances to prevent them from being called without their original context.
- Error:
[method-unbinding]("Cannot getcounter.incrbecause propertyincrcannot be unbound...") - The Flow rewrite: Wrap the call in an arrow function to capture the
thiscontext.
2.
thisinside Object LiteralsFlow bans the use of
thisinside object literal definitions to prevent unsafe extraction.- Error:
[object-this-reference] - The Flow rewrite: Use the name of the object literal binding directly instead of
this.
Usage Class Object Literal thisallowedYes No Method extraction Banned Allowed (if no thisused)// 1. Class Method Extraction class Counter { count: number = 0; incr(): number { return ++this.count; } } const counter = new Counter(); const tick = counter.incr; // ERROR: [method-unbinding] const tickFixed = () => counter.incr(); // OK // 2. Object Literal `this` usage const counterObj = { count: 0, incr(): number { return ++this.count; } // ERROR: [object-this-reference] }; // Correct Flow rewrite for object literals: const counterObjFixed = { count: 0, incr(): number { return ++counterObjFixed.count; } }; const tickObj = counterObjFixed.incr; // OK- Error: