【実務・中級編】 読み取り専用配列(ReadonlyArray) – TypeScript実践ガイド

エンジニアの皆さん、お疲れ様です。

今日はTypeScriptの「型」の中でも、特に「データの不変性(Immutability)」を担保するための守護神、`ReadonlyArray`と`const`アサーションについて語りましょう。

中級クラスのエンジニアなら、「配列の要素をいじらせたくない」という場面には何度も遭遇しているはずです。「とりあえず`any`で逃げる」とか、「あとで戻すからいいや」と安易にミュータブルな状態を放置した結果、どこかで意図せぬ副作用が発生してバグの温床になる……そんな地獄絵図は、もう終わりにしましょう。

なぜ ReadonlyArray が「現場の救世主」なのか

まず基本から。TypeScriptの`ReadonlyArray`は、その名の通り「読み取り専用」の配列です。`push`も`pop`も`splice`も、配列を破壊的に書き換えるメソッドは一切封じられます。

「え、わざわざ型を分けるの面倒くさくない?」と思うかもしれませんが、ここがTypeScriptの真骨頂です。ブラウザのJavaScriptエンジン(V8など)は実行時に型を消し去りますが、開発中に「これは書き換えてはいけないデータだ」とエディタに警告させることが、バグを未然に防ぐ最大の防御策になるんです。

実践的な使い分け:ReadonlyArray vs constアサーション

現場で最もよく使うのがこの2つのアプローチです。

1. ReadonlyArray型を明示する

関数の引数などで「この関数内では配列を破壊しないでね」という意図を明確にする場合に使います。

/

  • ユーザーIDリストを受け取って表示するだけの安全な関数

/
function displayUserIds(ids: ReadonlyArray): void {
// ids.push(999); // ここでコンパイルエラー!「プロパティ ‘push’ は存在しません」

ids.forEach(id => {
console.log(`User ID: ${id}`);
});
}

const myIds = [1, 2, 3];
displayUserIds(myIds); // 普通の配列もReadonlyArrayとして渡せます

2. constアサーション (`as const`)

設定値や固定の定数配列を扱う際、最強の武器になるのが`as const`です。これを使うと、配列の要素がすべて「リテラル型」として推論され、かつ全体が`readonly`になります。

// 普通の定数定義だと、後から変更できてしまう
const roles = [‘admin’, ‘editor’, ‘guest’];
roles[0] = ‘hacker’; // TypeScriptは文句を言わない(型がstring[]のため)

// as const をつけると?
const ROLES = [‘admin’, ‘editor’, ‘guest’] as const;

// ROLES[0] = ‘hacker’; // 即座にエラー!
// さらに、型も単なる string[] ではなく readonly [“admin”, “editor”, “guest”] になる
type Role = typeof ROLES[number]; // “admin” | “editor” | “guest” のUnion型が抽出できる!

この`as const`の恩恵は計り知れません。マジックナンバーや固定の選択肢を扱う際、「変更不可」と「型の抽出」を同時にこなせるので、コードの堅牢性が段違いになります。

ブラウザが裏側でどう処理しているか

少し技術の深層に触れましょう。JavaScriptの配列は本質的にオブジェクトです。`ReadonlyArray`は、TypeScript側で「特定のプロパティ(push, popなど)へのアクセスを禁止するインターフェース」を被せているだけに過ぎません。

ランダムアクセスやプロパティの書き換えをTypeScriptが禁止しているだけで、生成されたJavaScriptコードが実行される時、ブラウザ側のメモリ上では依然として普通の配列として存在しています。つまり、パフォーマンスへのオーバーヘッドはゼロ。TypeScriptの型定義を厳格にすることは、ランタイムに負担をかけず、開発者の脳の負担だけを減らすという、非常に高コスパなエンジニアリングなのです。

シニアからのアドバイス:現場でこう使え

もしあなたが既存のプロジェクトで「この配列、どこで書き換えられたのか追跡できない!」と苦しんでいるなら、まずは関数の引数に`ReadonlyArray`を強制的に導入するところから始めてください。

  • APIからのレスポンスを格納する変数: 積極的に`as const`や`ReadonlyArray`でラップする。
  • 設定ファイルや定数リスト: `as const`は必須。Enumの代わりに使うのが現代のベストプラクティスです。
  • 副作用を伴う関数: 引数に渡される配列は、原則としてすべて`ReadonlyArray`にする。

TypeScriptを使いこなすということは、コンパイラを自分の味方につけ、自分自身が「将来の自分」という一番の敵からコードを守るための儀式でもあります。

「動けばいい」コードから「壊れない」コードへ。まずは今日書くコードの配列の一つから、`Readonly`の魔法をかけてみてください。驚くほどコードが落ち着きを取り戻すはずですよ。

それでは、また現場でお会いしましょう。ハッピー・コーディング!

コメント

タイトルとURLをコピーしました