Skip to main content

Const Expressions

Sometimes it is useful to specifiy that a literal expression is expected to be immutable. In such cases, you can annotate the expression with the as const modifier. We refer to these expressions as const-expressions.

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 prop 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;
2:11-2:11: The `as const` assertion can only be used on string, numeric, boolean, object, or array literals. [unsupported-syntax]

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

export const STATUS = {
INIT: 'INIT',
LOADING: 'LOADING',
SUCCESS: 'SUCCESS',
ERROR: 'ERROR',
} 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

type State =
| { +kind: typeof STATUS.INIT; }
| { +kind: typeof STATUS.LOADING; progress: number; }
| { +kind: typeof STATUS.SUCCESS; result: string; }
| { +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.

Adoption of as const syntax

To use the as const syntax, you need to upgrade your infrastructure: