const assertion in TypeScript (as const)


TypeScript has quite expressive type system that can be little tempting for some coming from a different language like J*va. And to properly explain how as const works, I need to dig deeper into some of the basic language features.

Literal Types

TypeScript allows us to not only have strings, but also more concrete versions of strings, called literal types:

type Small = 'S';
type Medium = 'M';
type Large = 'L';

const size: Small = 'S';

Same goes for number and boolean:

type Zero = 0;
const foo: Zero = 0;

type Yes = true;
const bar: Yes = true;

Widening

Any time you create const variable with primitive value, TypeScript will use the literal type instead:

const isAuthorizedUser = true;
//    ^? const isAuthorizedUser: true

But when declaring variable using let, TypeScript will choose a different type:

let isAuthorizedUser = true;
//  ^? let isAuthorizedUser: boolean

This behaviour, when TypeScript infers more generic type instead of a literal one, is called widening.

You might assume that narrowing is the same as “not widening”, but it isn’t. Narrowing refers to process of refining variable type using type checks and/or type guards, while “not widening” is using a literal type instead of generic one.

const assertion

Finally, we can see how const assertion works:

  • Literals are no longer widened, even variables declared using let
let foo = 'bar' as const;
//  ^? let foo: 'bar'
  • Objects get readonly properties, all literal properties are not widened
const cart1 = { total: 99.99, shipping: { international: true, price: 9.99 } };
//    ^? const cart1: { total: number, shipping: { international: boolean, price: number } }
const cart2 = { total: 109.99, shipping: { international: true, price: 9.99 } } as const;
//    ^? const cart2: { readonly total: 109.99, readonly shipping: { readonly international: true, readonly price: 9.99 } }
  • Arrays become readonly tuples
const list1 = [1, 'Mary', true];
//    ^? const list1: (string | number | boolean)[]

const list2 = [1, 'Mary', true] as const;
//    ^? const list2: [1, 'Mary', true]