Skip to content

Latest commit

 

History

History
200 lines (156 loc) · 8.26 KB

readonly.md

File metadata and controls

200 lines (156 loc) · 8.26 KB

Item 14: Use readonly to Avoid Errors Associated with Mutation

Things to Remember

  • If your function does not modify its parameters, declare them readonly (arrays) or Readonly (object types). This makes the function's contract clearer and prevents inadvertent mutations in its implementation.
  • Understand that readonly and Readonly are shallow, and that Readonly 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 and readonly: the former prevents reassignment, the latter prevents mutation.

Code Samples

function printTriangles(n: number) {
  const nums = [];
  for (let i = 0; i < n; i++) {
    nums.push(i);
    console.log(arraySum(nums));
  }
}

💻 playground


function arraySum(arr: number[]) {
  let sum = 0, num;
  while ((num = arr.pop()) !== undefined) {
    sum += num;
  }
  return sum;
}

💻 playground


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.

💻 playground


interface FullyMutableName {
  first: string;
  last: string;
}
type FullyImmutableName = Readonly<FullyMutableName>;
//   ^? type FullyImmutableName = {
//        readonly first: string;
//        readonly last: string;
//      }

💻 playground


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

💻 playground


type T = Readonly<Outer>;
//   ^? type T = {
//        readonly inner: {
//          x: number;
//        };
//      }

💻 playground


const date: Readonly<Date> = new Date();
date.setFullYear(2037);  // OK, but mutates date!

💻 playground


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;
}

💻 playground


interface ReadonlyArray<T> {
  readonly length: number;
  // (non-mutating methods)
  toString(): string;
  join(separator?: string): string;
  // ...
  readonly [n: number]: T;
}

💻 playground


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[]'

💻 playground


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[]'.
  }
}

💻 playground


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;
}

💻 playground


function arraySum(arr: readonly number[]) {
  let sum = 0;
  for (const num of arr) {
    sum += num;
  }
  return sum;
}

💻 playground