Skip to content

Flow to TypeScript Declarations — Numio

Converts Flow code to TypeScript with flow-to-ts, then emits only a .d.ts declaration file with the TypeScript compiler. Bodies are removed, and parameters with default values become optional.

A package is written in Flow and you want to ship TypeScript typings for TypeScript consumers without migrating the source.

Flow

TypeScript Declarations

When to use it

  • A package is written in Flow and you want to ship TypeScript typings for TypeScript consumers without migrating the source.
  • You want to compare a Flow module's exported API with its TypeScript equivalent.

How to use

  1. Paste a Flow module into the Flow panel.
  2. It is posted to /api/flow-to-typescript with declarationOnly set; the server converts it to TypeScript and emits declarations.
  3. Copy the result from the TypeScript Declarations panel into an index.d.ts next to your package entry point.

Flow

// @flow
export type Point = { x: number, y: number };
export type Shape = 'circle' | 'square';
export type Label = ?string;
export const ORIGIN: Point = { x: 0, y: 0 };

export function distance(a: Point, b: Point = ORIGIN): number {
  return Math.hypot(a.x - b.x, a.y - b.y);
}

export class Canvas {
  shapes: Shape[] = [];
  draw(shape: Shape, at?: Point): void { this.shapes.push(shape); }
}

TypeScript declarations (.d.ts)

export type Point = {
  x: number
  y: number
}
export type Shape = "circle" | "square"
export type Label = string | null | undefined
export declare const ORIGIN: Point
export declare function distance(a: Point, b?: Point): number
export declare class Canvas {
  shapes: Shape[]
  draw(shape: Shape, at?: Point): void
}

Details and limitations

The page sends your input to a server route (/api/flow-to-typescript) for conversion; it is not processed only in the browser. Next.js API routes accept request bodies up to 1 MB by default.

TypeScript's ES2020 and DOM library files are loaded, so Array<T>, Promise, Date and $ReadOnlyArray (converted to ReadonlyArray) appear in the declarations as written.

Maybe types become T | null | undefined, as with Label in the example.

Only one file is compiled; imported types are not resolved.

FAQ

Are async functions and Promise return types kept?

Yes. export async function load(id: number): Promise<Tag> becomes export declare function load(id: number): Promise<Tag>.

What happens to default parameter values?

They are removed and the parameter becomes optional: b: Point = ORIGIN becomes b?: Point.

Are type aliases kept?

Yes. Exported type aliases appear in their converted TypeScript form. The page formats the result with Prettier, so it uses two-space indentation and no semicolons.

Specifications and references