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.
CodePudding user response:
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
.
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.