Best practices with const assertions in TypeScript
Tags: typescript, webdev, tutorial, beginners
Published: 2023-01-03 18:38:14

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.
<!-- more -->
## const declarations

A **const declaration** only makes the **array reference** immutable.

**Example**

```ts
// '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];
```

## const assertions

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

**Example**

```ts
// 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];
```

## 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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze).

**Example**

```ts
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);
```

## Best Practice

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

```ts
// Protection at design-time
const myArray = [1, 2, 3] as const;
// Protection at runtime
Object.freeze(myArray);
```

## as const with objects

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

```ts
// 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;
```

## as const with strings

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

```ts
// 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 = '...';
```

## Alternative Options

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

**Example**

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

**Shorthand Syntax**

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

## Video Tutorial

{% youtube -jZy8SYUfkE %}