Skip to content

Unlocking TypeScript's Hidden Gem `as const`

Posted on:June 9, 2023

TypeScript is a powerful superset of JavaScript that brings static typing to the dynamic world of web development. While many developers are familiar with the basics, there are hidden gems within TypeScript that often go unnoticed. One such gem is the as const feature, which proves to be incredibly useful in various situations.

Table of contents

Open Table of contents

What is as const?

At its core, as const is a feature that makes objects immutable in TypeScript. Let’s delve into an example to understand its significance.

Suppose we have an object named fruits:

const fruits = {
  apple: "red",
  banana: "yellow",
  orange: "orange",
};

By default, TypeScript infers the types of the properties in fruits as strings. Now, imagine a situation where we want to create a function that accepts any of these fruits as an argument. Without as const, we would encounter redundancy and potential maintenance challenges.

function eatFruit(fruit: "apple" | "banana" | "orange") {
  // Function logic here
}

Here, we have duplicated the fruit values, introducing a source of truth discrepancy. If the fruits object is modified, we need to remember to update this function as well.

The Magic of as const

Enter as const. By applying this modifier to the fruits object, TypeScript treats it as a read-only object, preventing any modifications. This not only enhances code robustness but also improves type inference.

const fruits = {
  apple: "red",
  banana: "yellow",
  orange: "orange",
} as const;

Now, attempting to modify any property of fruits will result in a compilation error. TypeScript knows that these values are constants and enforces immutability.

fruits.apple = "green";
// Cannot assign to 'apple' because it is a read-only property.

What about Object.freeze?

Indeed, Object.freeze can be used to achieve a similar effect as as const in terms of making objects immutable. However, there are some distinctions between the two approaches, both in terms of behavior and usage.

Object.freeze:

const deepFruits = {
  apple: { color: "red" },
  banana: { color: "yellow" },
  orange: { color: "orange" },
};

Object.freeze(deepFruits);

// Attempting to modify the nested object is allowed
deepFruits.apple.color = "green"; // No error at runtime

as const:

const deepFruits = {
  apple: { color: "red" },
  banana: { color: "yellow" },
  orange: { color: "orange" },
} as const;

// TypeScript catches the error during compilation
deepFruits.apple.color = "green"; // Compilation error

Type Extraction with typeof and keyof

The real magic of as const becomes apparent when combined with typeof and keyof. Suppose we want to create a function that dynamically accepts any of the fruits without hardcoding them. We can achieve this by extracting the type and keys of the fruits object.

type FruitKeys = keyof typeof fruits;

The FruitKeys type now represents the union of all keys in the fruits object. This enables us to create a more dynamic eatFruit function:

function eatFruit(fruit: FruitKeys) {
  // Function logic here
}

This improvement ensures that our function automatically adapts to changes in the fruits object, eliminating the need for manual updates.

Conclusion

In conclusion, the as const feature in TypeScript is a hidden gem that enhances code quality, promotes immutability, and enables dynamic typing. By combining it with typeof and keyof, developers can create more flexible and resilient code. Next time you find yourself working with constants or objects with fixed properties, consider unleashing the power of as const to elevate your TypeScript experience.