Skip to Content
Course content

96: Const Assertions with as const

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I've seen this happen a dozen times when developers are setting up configuration files or constants for their apps. You define a set of values that you know will never change, you use const to declare the object, and then you're baffled when TypeScript screams at you the moment you try to use one of those values in a function.

type UserRole = 'admin' | 'editor' | 'viewer';

const ROLE_CONFIG = {
  ADMIN: 'admin',
  EDITOR: 'editor',
  VIEWER: 'viewer',
};

function assignRole(role: UserRole) {
  console.log(`Assigning role: ${role}`);
}

// ❌ TypeScript Error: 
// Argument of type 'string' is not assignable to parameter of type 'UserRole'.
assignRole(ROLE_CONFIG.ADMIN); 

The "String Widening" Headache

If you're looking at that code and thinking, "But I used const! Why does TypeScript think ROLE_CONFIG.ADMIN is just a generic string?", you've run into type widening.

Here is the deal: when you declare an object with const, TypeScript ensures you can't reassign the entire object to something else. However, it doesn't assume the properties inside that object are immutable. TypeScript assumes that you might want to change ROLE_CONFIG.ADMIN = 'something else' later on. Because of that, it "widens" the type from the literal 'admin' to the general type string.

Since UserRole is a strict union of three specific strings, a generic string is too broad. TypeScript can't guarantee that ROLE_CONFIG.ADMIN hasn't been changed to 'super-user' by some other piece of code, so it blocks the call to assignRole to keep you safe.

Locking it down with as const

To fix this, we use a const assertion. By adding as const to the end of the object expression, you're telling TypeScript: "Don't widen these types. Treat every single property as a literal, and make the whole thing read-only."

type UserRole = 'admin' | 'editor' | 'viewer';

const ROLE_CONFIG = {
  ADMIN: 'admin',
  EDITOR: 'editor',
  VIEWER: 'viewer',
} as const; // <--- The magic happens here

function assignRole(role: UserRole) {
  console.log(`Assigning role: ${role}`);
}

// ✅ This now works perfectly!
assignRole(ROLE_CONFIG.ADMIN); 

When you add as const, a few things happen under the hood:

  • The type of ROLE_CONFIG.ADMIN is no longer string; it is exactly 'admin'.
  • The object becomes readonly. If you try to do ROLE_CONFIG.ADMIN = 'editor', TypeScript will throw an error because you've explicitly told it this structure is immutable.
  • If this were an array, it would be converted from a mutable array (like string[]) to a readonly tuple.

I generally use as const whenever I'm creating a lookup table, a set of fixed options, or any configuration object that serves as the "source of truth" for types elsewhere in my app. It saves you from having to manually type every single property of your config object while still getting 100% type safety.




📋 Practical Task

Refactoring the Theme Palette Configuration

You are working on a UI library where the theme colors are strictly defined. Currently, the THEME_PALETTE object is causing type errors because its values are being widened to string.

Your Task: Apply a const assertion to the THEME_PALETTE object so that the applyColor function accepts the palette values without error. Ensure the object remains read-only.

type ThemeColor = 'primary-blue' | 'secondary-grey' | 'accent-gold';

const THEME_PALETTE = {
  PRIMARY: 'primary-blue',
  SECONDARY: 'secondary-grey',
  ACCENT: 'accent-gold',
};

function applyColor(color: ThemeColor) {
  console.log(`Applying color: ${color}`);
}

// This is currently failing! Fix it using 'as const'.
applyColor(THEME_PALETTE.PRIMARY); 
Rating
0 0

There are no comments for now.

to be the first to leave a comment.