Const Expressions
The as const modifier tells Flow to infer the narrowest possible type for a literal expression — literal types for primitives and read-only types for containers.
1const x = 42 as const; // type is 42, not number2const a = [1, 2] as const; // type is Readonly<[1, 2]>, not Array<number>3const o = {x: 1} as const; // type is Readonly<{x: 1}>, not {x: number}When to use this
Use as const when you need Flow to preserve exact literal values and read-only structure — for example, keeping specific string values in a configuration object or ensuring an array is treated as a fixed-length tuple. If you only need an array or object literal to be read-only without narrowing to literal types, use the Readonly utility type instead.
Typing for Const Expressions
The inferred type of const-expressions is the singleton type for primitive values and the read-only versions for container types. Array literals are inferred as tuple types.
Here are some examples of primitive values:
42 as const; // inferred type is 42
"hello" as const; // inferred type is "hello"
Containers become read-only and the modifier is applied deeply
{ f: 42 } as const; // {+f: 42}
[42, "hello"] as const; // Readonly<[42, "hello"]>
{ f: { g: 42 } } as const; // {+f: {+g: 42}}
Note that the effect of the modifier does not persist through variables. For example in
const nonConstObject = { g: 42 };
const constObject = { f: nonConstObject } as const;
the type of nonConstObject will be {g: number} and the type of constObject will
be {+f: {g: number}}. In other words, only the top-level property f will
be read-only.
Finally, it is an error to apply as const to non-literal expressions:
1const x = 1;2const y = x as const;Typical const-expression example
A common pattern where const-expressions are useful is in enum-like structures that are not expected to be mutated. For example
1export const STATUS = {2 INIT: 'INIT',3 LOADING: 'LOADING',4 SUCCESS: 'SUCCESS',5 ERROR: 'ERROR',6} as const;The type of STATUS.INIT is "INIT", the type of STATUS.LOADING is "LOADING" and so on.
With this definition it is also possible to effectively lift the values of the various fields to type annotations. For example:
1const STATUS = {2 INIT: 'INIT',3 LOADING: 'LOADING',4 SUCCESS: 'SUCCESS',5 ERROR: 'ERROR',6} as const;7
8type State =9 | { +kind: typeof STATUS.INIT; }10 | { +kind: typeof STATUS.LOADING; progress: number; }11 | { +kind: typeof STATUS.SUCCESS; result: string; }12 | { +kind: typeof STATUS.ERROR; msg: string; };Without the use of as const the type typeof STATUS.INIT would be string, which
would make it unsuitable as a distinguishing tag in a disjoint union.
Const Expressions for React JSX ≥0.284
Check out render types and react type references if you are not familiar with React typing in Flow.
Since version 0.284, Flow will infer a more general type for React JSX elements by default.
1import * as React from 'react';2
3const div1 = <div />; // `<div />` has type `React.MixedElement`4declare component Foo();5const foo1 = <Foo />; // `<Foo />` has type `renders Foo`However, if the JSX element is in a position that can be contextually typed, Flow will infer a more specific type, so it can be accepted by more restrictive APIs:
1import * as React from 'react';2
3type ExactDiv = ExactReactElement_DEPRECATED<'div'>;4declare function onlyAllowsExactDiv(div: ExactDiv): void;5declare component OnlyAllowsExactDiv(children: ExactDiv);6
7const divGeneral = <div />;8onlyAllowsExactDiv(divGeneral); // Errorincompatible-typeCannot call onlyAllowsExactDiv with divGeneral bound to div because in type argument ElementType [1]: component [2] is incompatible with string literal div [3].incompatible-typeCannot call onlyAllowsExactDiv with divGeneral bound to div because in type argument ElementType [1]: string literal div [2] is incompatible with string [3].incompatible-typeCannot call onlyAllowsExactDiv with divGeneral bound to div because in type argument P [1]: unknown [2] is incompatible with props of string literal div [3].9<OnlyAllowsExactDiv>{divGeneral}</OnlyAllowsExactDiv>; // Errorincompatible-typeCannot create OnlyAllowsExactDiv element because in property children > type argument ElementType [1]: component [2] is incompatible with string literal div [3].incompatible-typeCannot create OnlyAllowsExactDiv element because in property children > type argument ElementType [1]: string literal div [2] is incompatible with string [3].incompatible-typeCannot create OnlyAllowsExactDiv element because in property children > type argument P [1]: unknown [2] is incompatible with props of string literal div [3].10onlyAllowsExactDiv(<div />); // OK11<OnlyAllowsExactDiv><div /></OnlyAllowsExactDiv>; // OKIf you want to have the jsx to have a more specific type in a position that cannot be contextually typed, you can wrap it with as const:
1import * as React from 'react';2
3type ExactDiv = ExactReactElement_DEPRECATED<'div'>;4declare function onlyAllowsExactDiv(div: ExactDiv): void;5declare component OnlyAllowsExactDiv(children: ExactDiv);6
7const divSpecific = <div /> as const;8onlyAllowsExactDiv(divSpecific); // OK9<OnlyAllowsExactDiv>{divSpecific}</OnlyAllowsExactDiv>; // OKAdoption of as const syntax
To use the as const syntax, you need to upgrade your infrastructure:
- Flow and Flow Parser: 0.256+
- Prettier: 3.1+
- Babel: use the babel-plugin-syntax-hermes-parser plugin version 0.19+, see our Babel guide for more details.
- ESLint: use hermes-eslint plugin version 0.19+, see our ESLint guide for more details.
const Type Parameters
Sometimes it is useful to specify that an argument to a function is always expected
to be a const-expression. In such cases, you can annotate the type parameter with
the const modifier. We refer to these type parameters as const-type parameters.
When are const type parameters useful?
One example is when you want to enforce that all arguments passed to a function
foo with signature
declare function foo<X>(x: X): X;
need to be treated as const-expressions. One way to support this is by always
calling foo with as const on its argument:
1declare function foo<X>(x: X): X;2
3const x1 = foo({ f: 42 } as const);4const x2 = foo([42, "hello"] as const);The variables x1 and x2 will have the types {+f: 42} and Readonly<[42, "hello"]>,
respectively.
To avoid repeating and potentially forgetting to pass as const, you can use the
const modifier on type parameter X:
1declare function constFoo<const X>(x: X): X;2
3const y1 = constFoo({ f: 42 });4const y2 = constFoo([42, "hello"]);The variables y1 and y2 will have the same type as x1 and x2, respectively.
Adoption of const type parameter syntax
To use the as const syntax, you need to upgrade your infrastructure:
- Flow and Flow Parser:
- 0.267 and 0.268 and passing the
experimental.const_type_params=trueflag in the flowconfig - 0.269+ without the flag.
- 0.267 and 0.268 and passing the
- Prettier: 3.5+
- Babel: use the babel-plugin-syntax-hermes-parser plugin version 0.26+, see our Babel guide for more details.
- ESLint: use hermes-eslint plugin version 0.26+, see our ESLint guide for more details.
See Also
- Literal Types — the literal types that
as constinfers for primitive values - Tuples —
as conston arrays produces read-only tuple types - Type Casting — the
askeyword for general type assertions (distinct fromas const) - Generics —
consttype parameters for enforcing const-expression inference on function arguments - Variance —
as constproduces read-only (covariant) properties