【入門編】 ReadonlyとMutableユーティリティ型 – TypeScript実践ガイド

みなさん、こんにちは!TypeScriptの世界へようこそ。
日々コードを書いていると、「あれ?いつの間にかこのデータが勝手に書き換えられてバグになっている…」なんていう冷や汗モノの体験、一度はありませんか?

特にWeb制作やアプリ開発の現場では、「絶対に途中で変更されたくないデータ」と「ユーザーの入力に応じてどんどん書き換えたいデータ」の2種類が常に混ざり合って存在しています。

今回は、そんなデータの「書き換え(変更)」をスマートにコントロールするための強力な相棒、`Readonly`と`Mutable`について解説します。

「ユーティリティ型?なんだか難しそう…」と思われた方も、大丈夫ですよ!
身近な例えを使いながら、ゆっくり一つずつ分解して解説していきますね。

—

1. そもそも「Readonly」ってなに? 〜油性ペンで書いた賞状〜

まずは `Readonly`(リードオンリー=読み取り専用)から見ていきましょう。

直訳すると「読むことしかできない」という意味ですが、日常生活で例えるなら「油性ペンでしっかりと書かれた賞状や契約書」のようなものです。一度書いたら、後から消しゴムで消して書き直すことはできませんよね。

TypeScriptにおける `Readonly` も全く同じです。オブジェクトのプロパティ(中身の値)を「後から絶対に書き換えられない状態」にしてくれます。

なぜ Readonly が必要なの?

開発現場では、複数人のエンジニアが同じコードを触ります。
例えば、ECサイトで「商品の元の値段」を扱っているとき、誰かが間違ってその値を途中で書き換えてしまったら…大変なことになりますよね。

「この値は絶対に触らないでね!」と口頭やコメントで伝えるのは限界があります。人間は必ずミスをする生き物だからです。
そこでTypeScriptの出番です。型を使って「変更不可!」とシステム的にロックをかけておけば、うっかり書き換えようとした瞬間にエディタが赤線で教えてくれます。

サンプルコードで試してみよう!

実際にコードで見てみましょう。以下のコードをTypeScriptのエディタ(Playgroundなど)に貼り付けてみてください。

// — 1. 普通の型(変更可能)をつくる —
type Product = {
id: string;
name: string;
price: number;
};

// — 2. Readonly を使って「変更不可」の型につくり変える —
// Readonly と書くだけで、すべてのプロパティが読み取り専用になります!
type ReadonlyProduct = Readonly;

// 実際にデータを作ってみましょう(ショッピングサイトの商品データ)
const item: ReadonlyProduct = {
id: “item-001”,
name: “極上コーヒー豆 200g”,
price: 1500,
};

// データの「読み取り」は問題なくできます
console.log(item.name); // 出力: “極上コーヒー豆 200g”

// ❌ データを書き換えようとすると…TypeScriptが怒ってくれます!
// [エラー] 読み取り専用プロパティであるため、’price’ に代入することはできません。
item.price = 1200;

`Readonly<型名>` と囲むだけで、すべての項目に目 invisible な「鍵」がかかりました!これで誤って価格が変更される事故を未然に防ぐことができます。

—

2. 逆の操作「Mutable」ってなに? 〜鉛筆で書いた下書きメモ〜

さて、`Readonly` が「油性ペン(書き換え不可)」なら、`Mutable`(ミュータブル=可変)は「鉛筆で書いた下書きメモ(書き換え可能)」です。

プログラミングの世界では、値が変更できることを「Mutable(可変)」、変更できないことを「Immutable(不変)」と呼びます。

ここで一つ、面白い(そして最初につまずきやすい)事実をお伝えしますね。

実は… TypeScriptの標準機能には `Mutable` という名前のユーティリティ型が存在しない のです!

「えっ!? `Readonly` があるなら `Mutable` も用意しておいてよ!」と思いますよね。分かります、私も最初はそう思いました(笑)。

TypeScriptは「安全性を高める方向(変更できないようにする方向)」の型は標準でたくさん用意してくれているのですが、「保護を解除して変更可能にする方向」の型は、必要に応じて自分で作るスタイルをとっているんです。

でも、大丈夫ですよ! 自作すると言っても、たった数行の魔法のコードを書くだけで簡単に作ることができます。

—

3. ないなら作ろう!自作 `Mutable` 型の魔法

「一度 `Readonly` になった型(例えば外部のライブラリから届いた型など)を、自分の手元でフォーム編集するために一時的に変更可能にしたい」という場面は、現場でもよくあります。

さっそく、自作の `Mutable` 型を作ってみましょう!

鍵を外す魔法の記法 `-readonly`

TypeScriptには、`readonly` という鍵を取り外すための「打ち消しゴム」のような記法が存在します。それが `-readonly`(マイナス・リードオンリー)です。

// — 自作 Mutable 型の定義 —
// 「Tという型のすべてのプロパティから readonly を取り除いてね!」という意味です
type Mutable = {
-readonly [P in keyof T]: T[P];
};

一見すると難しそうな記号が並んでいますが、意味はとってもシンプルです!
`[P in keyof T]` で型の中身をひとつずつ順番に取り出し、その頭に `-readonly` を付けることで「読み取り専用の鍵を解除!」と命じているだけなんですね。

サンプルコードで動作確認!

では、先ほど鍵をかけた `ReadonlyProduct` の鍵を、自作の `Mutable` 型で解除してみましょう。

// — さきほどの自作 Mutable 型 —
type Mutable = {
-readonly [P in keyof T]: T[P];
};

// 1. もともと「変更不可」だった商品型
type ReadonlyProduct = {
readonly id: string;
readonly name: string;
readonly price: number;
};

// 2. 自作した Mutable を使って「変更可能」な型に変換!
type EditableProduct = Mutable;

// 実際にデータを作ってみます(編集画面などで使うデータ)
const editingItem: EditableProduct = {
id: “item-001”,
name: “極上コーヒー豆 200g”,
price: 1500,
};

// ⭕️ 今度はエラーになりません!自由に変更できます。
editingItem.price = 1800; // 値引きや値上げの編集がスムーズにできる!
editingItem.name = “【セール】極上コーヒー豆 200g”;

console.log(editingItem);

見事に鍵が外れて、自由に変更できるようになりましたね!

—

4. 初心者が絶対にはまる!「浅い(Shallow)保護」の罠

ここで、現場で誰もが一度は首をかしげる「つまずきポイント」を一つそっとフォローしておきますね。

実は、標準の `Readonly` は、一番上の階層(浅い部分)しか保護してくれません。

「えっ、どういうこと?」と思いますよね。具体例を見てみましょう。

type UserProfile = {
name: string;
// オブジェクトの中に、さらにオブジェクト(ネスト)がある場合
address: {
city: string;
};
};

// Readonly にしてみます
type ReadonlyUser = Readonly;

const user: ReadonlyUser = {
name: “たなか”,
address: {
city: “東京”,
},
};

// ❌ 一番上の ‘name’ は保護されているのでエラー(正しく防げている)
// user.name = “すずき”;

// ⚠️ でも!入れ子になった奥の ‘city’ は変更できてしまう…!?
user.address.city = “大阪”; // エラーにならない!

「Readonly にしたのに奥の値が書き換わっちゃった!」と最初は驚くかもしれません。
これは標準の `Readonly` が、処理を軽くするために「表面(1階層目)だけ鍵をかける」仕様になっているからです。

もし「家(一番上のオブジェクト)だけでなく、家の中にある金庫(ネストしたオブジェクト)の中身まで全部鍵をかけたい!」という場合は、再帰処理を使った `DeepReadonly` というテクニックを使ったり、有名なユーティリティライブラリ(`type-fest` など)を活用したりします。

今は「へぇ〜、標準の Readonly は表面だけをガードしてくれるんだな」くらいに知っておくだけで100点満点ですよ!

—

5. まとめ:型で意思表示をしよう!

今日学んだことを振り返ってみましょう。

1. `Readonly`:
データを「油性ペン」のように書き換え不可にする。うっかりバグを未然に防ぐ最高の守り神!
2. `Mutable` (自作):
TypeScript標準にはないけれど、`-readonly` という魔法の消しゴムを使えば簡単に自作できる。編集機能などに便利!
3. プログラミングはチームプレイ:
「この値は変えないでね」「ここは編集していいよ」という意思表示を、コメントではなく型で表現できるようになると、コードの安全性がグッと高まります。

TypeScriptの型システムは、あなたを縛り付けるルールではなく、未来の自分やチームの仲間をうっかりミスから守ってくれる「やさしいシートベルト」です。

最初は覚えることが多くて大変に思えるかもしれませんが、一歩ずつ進んでいけば絶対にマスターできますよ。焦らず、自分のペースでエディタにコードを打ち込みながら試してみてくださいね。

あなたのTypeScriptライフが、より楽しく、安全なものになることを心から応援しています!

コメント

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