website/docs/modernizing-legacy-syntax.md
Flow's syntax has evolved substantially over time, with many forms converging on TypeScript-compatible spellings. Many $-prefixed utility types have been renamed, some have been removed entirely, and some legacy syntactic forms have modern keyword equivalents. This page is the canonical reference for what each legacy form should become.
The forms fall into three groups:
Flow no longer recognizes these.
| Legacy Flow | Removed in | Modern rewrite |
|---|---|---|
$Diff<A, B> | 0.268.0 | Typically Omit<A, keyof B> or restructuring. Not always semantically identical — handle case by case. |
$Rest<A, B> | 0.267.0 | Same status as $Diff. Usually replaceable with Omit + spread. |
$PropertyType<T, K> | 0.266.0 | T[K] (indexed access). |
$ElementType<T, K> | 0.266.0 | T[K] (indexed access). |
$TupleMap<T, F> | 0.248.0 | Mapped type with the body of F inlined — e.g. $TupleMap<T, <V>(V) => Promise<V>> becomes {[K in keyof T]: Promise<T[K]>}. |
$TupleMapi<T, F> | 0.248.0 | Mapped type with key access and F inlined — e.g. {[K in keyof T]: [K, T[K]]}. |
%checks predicate functions | 0.248.0 | Type guards — e.g. function isString(x: unknown): boolean %checks becomes function isString(x: unknown): x is string. |
$Call<F, ...Args> | 0.247.0 | ReturnType<F> plus indexed access, or a conditional type with infer. |
$ObjMap<O, F> | 0.246.0 | Mapped type with F inlined — e.g. $ObjMap<O, <V>(V) => Promise<V>> becomes {[K in keyof O]: Promise<O[K]>}. |
$ObjMapi<O, F> | 0.246.0 | Mapped type with key access and F inlined — e.g. {[K in keyof O]: [K, O[K]]}. |
React.Element<typeof Foo> | 0.245.0 | Prefer render types when expressing composition constraints ("a Menu only renders MenuItems") — that is what most uses of React.Element<typeof Foo> were really expressing. For generic "any element / any node" positions, use React.MixedElement / React.Node. Reach for ExactReactElement_DEPRECATED<typeof Foo> only as an escape hatch when exact element identity is genuinely required. |
$Shape<T> | 0.206.0 | Partial<T>. Not always semantically identical — $Shape was permissive about depth in ways Partial is not, so migrate per call site rather than blindly renaming. |
$Partial<T> | 0.203.0 | Partial<T>. |
A modern equivalent exists. Some forms already error today; others still parse but are on a deprecation path.
| Legacy Flow | Modern Flow |
|---|---|
$Keys<T> | keyof T |
$ReadOnly<T> | Readonly<T> |
$NonMaybeType<T> | NonNullable<T> |
$ReadOnlyArray<T> | ReadonlyArray<T> |
$ReadOnlyMap<K, V> / $ReadOnlySet<T> / $ReadOnlyWeakMap<K, V> / $ReadOnlyWeakSet<T> | ReadonlyMap / ReadonlySet / ReadonlyWeakMap / ReadonlyWeakSet |
$Values<T> | Values<T> (renamed within Flow's own utilities) |
mixed | unknown |
| Legacy Flow | Modern Flow |
|---|---|
(x: T) cast | x as T |
<T: Bound> | <T extends Bound> |
{| a: number |} exact | {a: number} (exact is the default) |
+foo / -foo property variance sigils | readonly foo / writeonly foo keywords (writeonly is Flow-specific) |
+T / -T type parameter variance sigils | out T / in T keywords |
The form is still recognized by the type checker and does not error, but new code should avoid it.
| Type | Recommended approach |
|---|---|
$Exact<T> | Define the exact type first and derive inexact variants from it via object type spread ({...Exact, ...}). See $Exact<T> (Discouraged). |
$-prefixed utilities that are not deprecated {#toc-not-deprecated}A few $-prefixed types remain first-class Flow utilities — the $ is just part of the name, not a marker of being deprecated. They are esoteric and rarely encountered, but if you do hit one, keep using it. Examples include $KeyMirror<O> and $Exports<...>. See Utility Types for the current list.
$ObjMap / $TupleMap and friends.as cast syntax that replaces (x: T).T[K] form that replaces $PropertyType / $ElementType.