DEV Community

Cover image for Best practices with const assertions in TypeScript
Benny Code for TypeScript TV

Posted on • Originally published at typescript.tv

Best practices with const assertions in TypeScript

TypeScript 3.4 introduced const assertions, a feature that allows you to claim a value as immutable. This feature is particularly valuable in combination with array literals, as it prevents new values from being pushed into an existing array.

const declarations

A const declaration only makes the array reference immutable.

Example

// 'const' only makes the array reference immutable
const myArray = [1, 2, 3];
// Values of the array can still be removed
myArray.pop();
// ...or pushed
myArray.push(4);
// ... but reassignments become impossible
// TS2588: Cannot assign to 'myArray' because it is a constant.
myArray = [4];
Enter fullscreen mode Exit fullscreen mode

const assertions

With a const assertion you will get immutability for your array reference and array values at design-time.

Example

// A 'const assertion' protects the reference and elements of your array
const myArray = [1, 2, 3] as const;
// TS2339: Property 'pop' does not exist on type 'readonly [1, 2, 3]'.
myArray.pop();
// TS2339: Property 'push' does not exist on type 'readonly [1, 2, 3]'.
myArray.push(4);
// TS2588: Cannot assign to 'myArray' because it is a constant.
myArray = [4];
Enter fullscreen mode Exit fullscreen mode

Important note

One important thing to note is that const assertions are a TypeScript feature at design-time and do not actually make values immutable at runtime. This means that you will still be able to modify values at runtime using plain JavaScript. If you want to achieve a higher level of immutability, you can make use the Object.freeze() API.

Example

const myArray = [1, 2, 3];
// The static 'freeze' method makes your array immutable at runtime
Object.freeze(myArray);
// This will happen when you try to manipulate your array in JavaScript:
// Uncaught TypeError: Cannot add property 4, object is not extensible
myArray.push(4);
Enter fullscreen mode Exit fullscreen mode

Best Practice

You can combine a const assertion with Object.freeze to get type safety at design-time and runtime:

// Protection at design-time
const myArray = [1, 2, 3] as const;
// Protection at runtime
Object.freeze(myArray);
Enter fullscreen mode Exit fullscreen mode

as const with objects

A const assertion can also protect an object literal by marking its properties as readonly:

// Protect your object properties from changes at design-time
const objectLiteral = {
  age: 35,
  name: 'Benny'
} as const;

// TS2540: Cannot assign to 'age' because it is a read-only property.
objectLiteral.age = 30;
Enter fullscreen mode Exit fullscreen mode

as const with strings

You can narrow the type for a string to a specific string using as const:

// Don't use an explicit return type to make use of type inference
function getName(firstName: string, lastName: string) {
  return `${firstName} ${lastName}` as const;
}

let displayName = getName('Benny', 'Code');
// TS2322: Type '"..."' is not assignable to type '`${string} ${string}`'.
displayName = '...';
Enter fullscreen mode Exit fullscreen mode

Alternative Options

You can use the ReadonlyArray type of TypeScript 3.4 to disallow array value modifications at design-time:

Example

const array: ReadonlyArray<number> = [1, 2, 3];
// TS2339: Property 'pop' does not exist on type 'readonly number[]'.
array.pop();
Enter fullscreen mode Exit fullscreen mode

Shorthand Syntax

const array: readonly number[] = [1, 2, 3];
// TS2339: Property 'pop' does not exist on type 'readonly number[]'.
array.pop();
Enter fullscreen mode Exit fullscreen mode

Video Tutorial

Top comments (0)