Flow Static Type Checker

repository·main·Indexed 12 days ago

https://github.com/facebook/flow

A 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.

Tokens
250.4K
Snippets
1K
Records
1.2K
Agent score
97%

What's inside Flow

  1. Use Flow types for the Flow-ESTree spec

    main
    The flow-estree package 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.
  2. Compare Flow and TypeScript syntax and semantics

    main

    Flow and TypeScript share significant overlap in syntax and vocabulary, including conditional types, mapped types, type guards, keyof, as const, unknown, and Readonly. 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, and renders.
    • Tooling Scope: Unlike tsc, the flow binary 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 strict enabled.

    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');
  3. What is a Library Definition (libdef)?

    main

    A 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.

  4. Flow syntax and TypeScript compatibility

    main

    Flow provides a syntax highly compatible with TypeScript, supporting advanced type constructs such as keyof, readonly properties, unknown types, indexed access types T[K], and extends generic 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');
  5. Handle Array invariance with ReadonlyArray

    main

    Mutable arrays (Array<T>) are invariantly typed. This means you cannot pass an Array<string> to a function expecting Array<string | number> because the function could mutate the array by pushing a number, which would invalidate the original Array<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);
  6. Create Polymorphic Component Types

    main

    Polymorphic 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>
    );
  7. Compare Tuples and Arrays

    main

    In Flow, tuples and arrays are distinct types and are not interchangeable in most cases:

    1. Array to Tuple: An Array<T> cannot be passed into a tuple because Flow cannot guarantee the array's length.
    2. Tuple to Array: A tuple cannot be passed into an Array<T> because it would allow unsafe mutations like .push().
    3. 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; use Readonly<[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!
  8. Handle `this` binding in classes and object literals

    main

    Flow enforces strict rules to prevent runtime crashes caused by this being 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 get counter.incr because property incr cannot be unbound...")
    • The Flow rewrite: Wrap the call in an arrow function to capture the this context.

    2. this inside Object Literals

    Flow bans the use of this inside 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.
    UsageClassObject Literal
    this allowedYesNo
    Method extractionBannedAllowed (if no this used)
    // 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