What is the difference between readonly and const assertion for class properties?

Issue

Could someone clarify to me, what is the difference between the two following notations in TypeScript?

class Test {
  readonly ANSWER = 42;
}

and

class Test {
  ANSWER = 42 as const;
}

The first is a readonly modifier, while the second is called const assertion. Both concepts are well-documented and explained on multiple places, yet they both seem to do the same thing to me, in this simple case at least, i.e. making the property immutable. Even their combination seems to be valid:

class Test {
  readonly ANSWER = 42 as const;
}

So why are they both allowed syntax and is there any advantage or a disadvantage in using one over the other?

To clarify further: I am fully aware of the difference between regular const and readonly. I am specifically interested in the difference between const assertion and readonly.

Solution

readonly ANSWER = 42 describes the assignability of a property. Typescript is smart enough to know that no other value can ever be assigned, so it’s safe to infer as specific a type as possible. In this case 42 instead of number

Where ANSWER = 42 as const describes the type of a value and forces a constant inference of the type, instead of the default of number.

They are very different things.

Here’s a better example:

class Test1 {
  ANSWER = 42 as const;

  set42() {
    this.ANSWER = 42 // fine
  }
}

class Test2 {
  readonly ANSWER = 42;

  set42() {
    this.ANSWER = 42 // error
  }
}

Here Test1‘s ANSWER has a value type of the numeric literal value 42. You can assign anything you want to that property as long as it’s 42. Without as const the type would be inferred as number instead, and allow any number to assigned.

However, Test2‘s ANSWER cannot be reassigned, even if it’s the right type because it is readonly.

Playground


The difference is more apparent with more complex types:

class Test1 {
  obj = { a: 123 } as const;
  // Test1.obj: { readonly a: 123; }

  setObj() {
    this.obj = { a: 123 } // fine
  }

  setObjProp() {
    this.obj.a = 456 // error
  }
}

class Test2 {
  readonly obj = { a: 123 };
  // Test2.obj: { a: number; }

  setObj() {
    this.obj = { a: 123 } // error
  }

  setObjProp() {
    this.obj.a = 456 // fine
  }
}

class Test3 {
  readonly obj = { a: 123 } as const;
  // Test3.obj: { readonly a: 123; }

  setObj() {
    this.obj = { a: 123 } // error
  }

  setObjProp() {
    this.obj.a = 123 // error
  }
}

Here the readonly modifier on Test2 fails to infer the more specific type of { a: 123 } and instead falls back to { a : number }.

But while the Test2.obj property is readonly, the object assigned it remains mutable. So this.obj.a = 456 is valid, and why { a: number } is the inferred type.

Where with as const the object itself is readonly, so it cannot be mutated.

When you combine both readonly and as const you declare an immutable property with a value that is also immutable.

Playground

Answered By – Alex Wayne

Answer Checked By – Mildred Charles (AngularFixing Admin)

Leave a Reply

Your email address will not be published.