Reflow

Babel plugin to transpile a Flow typed codebase to TypeScript.

CircleCI Coverage

This is still in an early stage and may break anytime!

Reflow enables you to migrate a whole Flow based project to TypeScript by transpiling the Flow type annotations to equivalent TypeScript code. While this reduces the effort to move a large code base to TypeScript drastically, it is still likely that you will need to manually fix new type errors afterwards. This program helps you with the tedious task to convert Flow syntax to TypeScript, but it can not magically fix type errors which occur after the migration.

See this repository for an excellent overview of the differences and similarities of Flow and Typescript.

Installation

yarn add --dev babel-plugin-reflow

Usage

CLI

This package includes a small CLI wrapper for the Babel plugin to recursively transpile a whole project directory. Install the package as project dependency and run npx reflow afterwards. Alternatively you might want to install Reflow globally so you can simply type reflow:

yarn global add babel-plugin-reflow

Usage is as follows:

$ npx reflow --help

Usage: reflow [OPTION]... <FILES OR DIRECTORIES ...>

Reflow

Options:
  -V, --version                    output the version number
  -d, --dry-run                    Perform a trial run printing to stdout instead of writing a file
  -e, --exclude-dirs <dirs ...>    List of recursively excluded directories (default: ["node_modules"])
  -i, --include-pattern <pattern>  Set the glob pattern for input files (default: "**/*.{js,jsx}")
  -r, --replace                    Process files in-place instead of creating new TS files next to the original JS files.
  -D, --replace-decorators         Replace class @decorators with wrapped function calls to avoid TypeScript errors
  -h, --help                       output usage information

Examples:
  $ reflow --replace src/
  $ reflow -d -i '**/__tests__/**/*.{js,jsx} src/
  $ reflow -exclude-patterns '**/__tests__/**/*','fixtures/*.js' src/

Programmatically

TODO

As Babel plugin (not recommended)

TODO

Transformations

Base types

The following examples have been chosen to show some of the syntax differences between Flow and TypeScript. Most syntax is similiar, but there are differences and also certain edge cases, which need to be handled while transpiling the types. A few types are not equivalently expressible in TypeScript and will result in a small loss of type information. See the list of unsupported Flow features in TypeScript below.

Type Flow TypeScript
Array type Array<number> Array<number>
Boolean literal type true true
Boolean type boolean boolean
Empty type empty never
Exact object type `{ p: number
Function type (string, {}) => number (p1: string, p2: {}) => number
Generic type annotation let v: <FlowType> let v: <TSType>
Generics type Generic<T: Super> = T type Generic<T extends Super> = T
Interface type interface { +p: number } interface { readonly p: number }
Intersection type type Intersection = T1 & T2 type Intersection = T1 & T2
Mixed type mixed unknown
Null literal type null null
Nullable type (Maybe) ?number `number
Number literal type 42 42
Number type number number
Object type { [string]: number } { [key: string]: number }
Opaque type opaque type Opaque = number type Opaque = number
String literal type 'literal' 'literal'
String type string string
This type this this
Tuple type [Date, number] [Date, number]
Type alias type Type = <FlowType> type Type = <TSType>
Type casting (t: T) (t as T)
Type exports / imports import type T from './types' import T from './types
Typeof type typeof undefined undefined
Union type `number null`
Void type void void

Utility types

Utility Type Flow TypeScript
Call $Call<F, T...> ReturnType<F>
Class Class<T> typeof T
Difference $Diff<A, B> Omit<A, keyof B>
Element type $ElementType<T, K> T[k]
Exact $Exact<T> T
Existential type * any
Keys $Keys<T> keyof T
None maybe type $NonMaybeType<T> NonNullable<T>
Object map $ObjMap<T, F>
Object map with key $ObjMapi<T, F>
Property type $PropertyType<T, k> T[k]
ReadOnly $ReadOnly<T> Readonly<T>
Rest $Rest<A, B>
Shape $Shape<T> Partial<T>
Tuple map $TupleMap<T, F>
Values $Values<T> T[keyof T]
Subtype deprecated
Supertype deprecated

*

Declarations

Declaration Flow TypeScript
Declare ES module export declare export default () => string const _default: () => string; export default _default;
Declare class declare class C {} declare class C {}
Declare function declare function f(number): any declare function f(p: number): any
Declare interface declare interface I {} declare interface I {}
Declare module declare module 'esmodule' {} declare module 'esmodule' {}
Declare module statement declare var v: string var v: string
Declare type alias declare type T: number declare type T = number
Declare variable declare var v: any declare var v: any

Unsupported: CommonJS export declarations.


Unsupported Flow features / syntax

The following Flow features are not equivalently expressible in TypeScript and need to be handled differently:

  • Constructor return types

    TypeScript intentionally doesn't support return types for constructor functions. These will be removed by Reflow.

  • Existential Type

    Flow's existential type has been deprecated and should be avoided. Still Reflow supports it and will transform it to any.

  • Function types with unnamed parameters

    In contrast to TypeScript, parameter names can be omitted in Flow. Therefore Reflow inserts parameter names automatically (p for a single parameter and p{i} for multiple ones).

    type FunctionType = ({}, Date) => string;             // Flow
    type FunctionType = (p1: {}, p2: Date) => string;    // TypeScript
    
  • Index signatures

    Flow allows any type for keys in index signatures, but Typescript only accepts string or number. Reflow will add index signatures both for string and number if a different type is specified in Flow.

    // Flow
    declare type KeyType;
    interface I = {
      [key: KeyType]: number
    }
    
    // TypeScript
    interface I = {
      [key: number]: number;
      [key: string]: number;
    }
    
  • Opaque Type

    Opaque types are not supported in TypeScript and are transformed to an ordinary type alias.

    opaque type T = number;  // Flow
    type T = number;         // TypeScript
    
  • Variance

    Flow's contravariance sigil - is not expressible in Typescript and will be omitted. However, TypeScript does support covariance for certain types (+ becomes readonly).

    // Flow
    interface I {
      +covariant: any;
      -contravariant: any;
    }
    
    // TypeScript
    interface I {
      readonly covariant: any;
      contravariant: any;
    }
    
  • $Call<F, T...>

    The $Call<F, T...> utility type is transformed to TypeScript's ReturnType<F>. Since this type only accepts the function type and not the function argument types, it is impossible to infer the return type of polymorphic functions. TypeScript will assume an unknown type then.

Supported syntax

This Babel plugin has built-in support for

  • React
  • JSX
  • Class properties (proposal)
  • Dynamic imports (proposal)
  • Decorators (proposal)

Development

Clone this repository and install the project dependencies:

yarn install

There are various npm scripts for different tasks:

yarn build          # Create a production build
yarn dev            # Build in development mode and watch for changes
yarn format         # Format the code with Prettier
yarn lint           # Run ESLint
yarn test           # Run fixture tests
yarn test:coverage  # Run the tests with coverage report
yarn tsc            # Check the types (via TypeScript)