zukucode
主にWEB関連の情報を技術メモとして発信しています。

TypeScript readonly配列で意図しない変更を防ぐ

関数へ配列を渡すと、その関数からpushやsortで元の配列を書き換えられます。読み取りだけをする関数では、引数をreadonlyにして意図を型で表します。

読み取り専用の引数にする

const total = (prices: readonly number[]): number =>
  prices.reduce((sum, price) => sum + price, 0);

const prices = [100, 200, 300];
console.log(total(prices)); // 600

totalの中でprices.push(400)を書くと型エラーになります。呼び出し元の配列は通常の配列でも渡せます。

逆に、関数の引数を変更可能な number[] にすると、as constで定義した値を渡せません。読み取りしか行わない関数なら、最初から readonly number[] を受け取る方が利用できる配列の範囲も広がります。

並び替えたいときはコピーする

const sortedPrices = (prices: readonly number[]): number[] =>
  [...prices].sort((a, b) => a - b);

sortは対象を変更するため、先にスプレッド構文でコピーします。

const source = [300, 100, 200];
const sorted = sortedPrices(source);
console.log(source); // [300, 100, 200]
console.log(sorted); // [100, 200, 300]

コピーが必要なのは、sortなど元の配列を書き換えるメソッドを使うときです。mapやfilterは新しい配列を返します。

どこまで守られるか

readonlyはTypeScriptの型チェックであり、実行時に配列を凍結しません。また、配列内のオブジェクトまで深く読み取り専用にするものでもありません。外部入力の改ざん防止には別の仕組みが必要です。

const products: readonly { name: string }[] = [{ name: 'ノート' }];
products[0].name = 'ペン'; // 要素内のプロパティは変更できる

関連記事