- If your function does not modify its parameters, declare them
readonly
(arrays) orReadonly
(object types). This makes the function's contract clearer and prevents inadvertent mutations in its implementation. - Understand that
readonly
andReadonly
are shallow, and thatReadonly
only affects properties, not methods. - Use
readonly
to prevent errors with mutation and to find the places in your code where mutations occur. - Understand the difference between
const
andreadonly
: the former prevents reassignment, the latter prevents mutation.
function printTriangles(n: number) {
const nums = [];
for (let i = 0; i < n; i++) {
nums.push(i);
console.log(arraySum(nums));
}
}
function arraySum(arr: number[]) {
let sum = 0, num;
while ((num = arr.pop()) !== undefined) {
sum += num;
}
return sum;
}
interface PartlyMutableName {
readonly first: string;
last: string;
}
const jackie: PartlyMutableName = { first: 'Jacqueline', last: 'Kennedy' };
jackie.last = 'Onassis'; // OK
jackie.first = 'Jacky';
// ~~~~~ Cannot assign to 'first' because it is a read-only property.
interface FullyMutableName {
first: string;
last: string;
}
type FullyImmutableName = Readonly<FullyMutableName>;
// ^? type FullyImmutableName = {
// readonly first: string;
// readonly last: string;
// }
interface Outer {
inner: {
x: number;
}
}
const obj: Readonly<Outer> = { inner: { x: 0 }};
obj.inner = { x: 1 };
// ~~~~~ Cannot assign to 'inner' because it is a read-only property
obj.inner.x = 1; // OK
type T = Readonly<Outer>;
// ^? type T = {
// readonly inner: {
// x: number;
// };
// }
const date: Readonly<Date> = new Date();
date.setFullYear(2037); // OK, but mutates date!
interface Array<T> {
length: number;
// (non-mutating methods)
toString(): string;
join(separator?: string): string;
// ...
// (mutating methods)
pop(): T | undefined;
shift(): T | undefined;
// ...
[n: number]: T;
}
interface ReadonlyArray<T> {
readonly length: number;
// (non-mutating methods)
toString(): string;
join(separator?: string): string;
// ...
readonly [n: number]: T;
}
const a: number[] = [1, 2, 3];
const b: readonly number[] = a;
const c: number[] = b;
// ~ Type 'readonly number[]' is 'readonly' and cannot be
// assigned to the mutable type 'number[]'
function printTriangles(n: number) {
const nums = [];
for (let i = 0; i < n; i++) {
nums.push(i);
console.log(arraySum(nums as readonly number[]));
// ~~~~~~~~~~~~~~~~~~~~~~~~~
// The type 'readonly number[]' is 'readonly' and cannot be
// assigned to the mutable type 'number[]'.
}
}
function arraySum(arr: readonly number[]) {
let sum = 0, num;
while ((num = arr.pop()) !== undefined) {
// ~~~ 'pop' does not exist on type 'readonly number[]'
sum += num;
}
return sum;
}
function arraySum(arr: readonly number[]) {
let sum = 0;
for (const num of arr) {
sum += num;
}
return sum;
}