Home > Enterprise >  How can I represent an immutable array of immutable objects?
How can I represent an immutable array of immutable objects?

Time:11-26

I'd like to define a class which takes as one of of its constructor parameters an array of objects, and I'd like to provide the guarantee that neither the array nor the objects in it will be modified. My current attempt uses the readonly modifier and the Readonly<T> generic and looks something like this:

export type Foo = { foo: string };

export class Bar {
  readonly foo: Foo;
  readonly bars: Array<Readonly<Bar>>;

  constructor(
    foo: Readonly<Foo>,
    bars: Readonly<Array<Readonly<Bar>>>,
  ) {
    this.foo = foo;
    this.bars = bars;
  }
}

(Playground link.)

However, this gives an error on the line this.bars = bars;, saying The type 'readonly Readonly<Bar>[]' is 'readonly' and cannot be assigned to the mutable type 'Readonly<Bar>[]'.ts(4104).

After some searching, I found a couple of answers which seem, if I understand them correctly, to indicate that mutable arrays and readonly/Readonly<T> arrays can't be assigned to one another.

How, then, can I represent the immutability contract I'm trying to express? I'm using Typescript 4.5.2 and my tsconfig.json is as follows:

{
  "compilerOptions": {
    "exactOptionalPropertyTypes": true,
    "forceConsistentCasingInFileNames": true,
    "isolatedModules": true,
    "noImplicitOverride": true,
    "noPropertyAccessFromIndexSignature": true,
    "noUncheckedIndexedAccess": true,
    "strict": true
  }
}

CodePudding user response:

I would use a ReadonlyArray.

export type Foo = { foo: string };

export class Bar {
  readonly foo: Foo;
  readonly bars: ReadonlyArray<Readonly<Bar>>;

  constructor(
    foo: Readonly<Foo>,
    bars: ReadonlyArray<Readonly<Bar>>,
  ) {
    this.foo = foo;
    this.bars = bars;
  }
}

In the statement readonly bars: ReadonlyArray<Readonly<Bar>>, the meaning of the different parts is as follows:

  • readonly states that the bars property is readonly, it prevents you from writing this.bars = whatever.
  • ReadonlyArray states that the array is readonly, it prevents you from writing this.bars[0] = whatever.
  • Readonly<Bar> states that the elements of the array are readonly, it prevents this.bars[0].foo = whatever.
  • Related