【入門編】 Readonlyによる読み取り専用化 – TypeScript実践ガイド

TypeScriptの世界へようこそ!チーフアーキテクトの私です。

フロントエンドの現場に立っていると、「うわ、誰だこのデータ書き換えたの…!?」というバグに頭を抱える夜が、エンジニアなら誰しも一度や二度(いや、もっと?)あるものです。アプリが複雑になってくると、あっちこっちのコンポーネントが勝手にデータを書き換えてしまい、状態がカオスになる。

そんな「不意打ちのデータ書き換え」という現場の悲劇を防ぐための強力な相棒が、今回紹介する `Readonly` です。

難しい言葉は抜きにして、さっそく一緒に紐解いていきましょう。大丈夫、一歩ずつ見ていけば絶対に怖くありませんよ!

—

1. イミュータブル(変更不可)ってなぁに?

TypeScriptでコードを書いていると、「イミュータブル」というちょっとカッコいい言葉を耳にすると思います。

難しく考える必要はありません。要するに「一度作ったら、二度と中身を変えられない(書き換え禁止)」という意味です。

身近な例えで考えてみましょう。
あなたがコンビニで買った「アイスクリームのパッケージ」を想像してください。蓋を開けてスプーンを入れたら中身を食べられますよね。でも、一度食べたアイスのカップに、別のプリンを「やっぱりこっちの方がいいや」と戻したりはできませんよね(物理的にはできるかもしれませんが、そういう問題ではなく…!笑)。

あるいは、役所で発行してもらう「住民票」を思い浮かべてみてください。そこに書いてある自分の住所や名前を、ペンで「やっぱりここ引っ越したから書き換えとこっと」と勝手に修正したら……犯罪になっちゃいますよね(笑)。

プログラムの世界でもこれと同じです。
「この大事な設定データは、絶対に途中で誰にも書き換えられたくない!」
「APIから取得したユーザー情報は、表示専用だから勝手にいじってほしくない!」

そんなときに使うのが、今回主役の `Readonly` なんです。

—

2. 通常のオブジェクトと `Readonly` の違い

まずは、普段よく書く普通のオブジェクトの姿を見てみましょう。

// 普通のユーザー情報の型
interface User {
name: string;
age: number;
}

// ユーザーデータを作ってみるよ
const myUser: User = {
name: “たろう”,
age: 25,
};

// 普通のオブジェクトなら、後から書き換えるのはお手のもの!
myUser.name = “次郎”; // 怒られません。スイスイ書き換わります。
myUser.age = 30; // こっちも問題なし!

普通のオブジェクトは、このようにいつでも自由にデータを塗り替えることができます。これはこれで便利なんですが、アプリが大きくなってくると「あれ? 今 `name` って誰が書き換えたんだっけ?」というデバッグの迷宮に入り込む原因になります。

そこで、登場するのが `Readonly` です。
使い方はとってもシンプル。型を囲うように `Readonly<...>` と包んであげるだけ。

// Readonlyで包んで、絶対に書き換えられない「お触り厳禁」の型にするよ
const safeUser: Readonly = {
name: “花子”,
age: 22,
};

// さあ、ここでデータを書き換えようとすると……?
// safeUser.name = “三郎”;
// ❌ ここでTypeScript先生が真っ赤な波線を出して怒ってくれます!
// 「おいおい、そいつは読み取り専用(readonly)だぞ!」と。

このように、`Readonly` は、指定したオブジェクトのすべてのプロパティ(項目)に「変更禁止!」という鍵をかけてくれる魔法のラッパー(包み紙)なんです。

—

3. 実務で出会う「うっかりミス」を防ぐ具体例

Web制作やアプリ開発の現場では、例えば「設定データ」や「初期データ」を扱うときによくこの `Readonly` が使われます。

実際の現場を少し覗いてみましょう。

// アプリ全体の基本設定を表す型
interface AppConfig {
readonly apiUrl: string; // ここに直接 readonly をつけることもできます
readonly timeout: number;
}

// または、すでにある型をまとめて Readonly で包むことも多いです!
interface Product {
id: number;
title: string;
price: number;
}

// カートに入れた商品の初期データ
const initialProduct: Readonly = {
id: 101,
title: “極上のTypeScript解説本”,
price: 3200,
};

// 【よくあるうっかりミス】
// 割引セールだからといって、うっかり価格を書き換えようとしてしまう…
// initialProduct.price = 2800; // ❌ エラー! TypeScriptがバグを未然に防いでくれた!

「えっ、じゃあセールで価格を変えたいときはどうするの?」って思いますよね。
大丈夫です。イミュータブルな世界の鉄則は、「元のデータをいじるな、新しいデータを作れ」です。

// セール品の新しいデータを作りたいときは、スプレッド構文(…)を使って新しくコピー&上書きしよう!
const saleProduct: Product = {
…initialProduct, // 既存のプロパティをパパッとコピーして
price: 2800, // 価格だけ新しくする!
};

console.log(saleProduct);
// { id: 101, title: “極上のTypeScript解説本”, price: 2800 } が完成!

このように、「元データを汚さない(イミュータブルを保つ)」ことで、アプリの動きが圧倒的に予測しやすくなり、バグの温床を綺麗に排除できるようになるんです。

—

4. チーフアーキテクトからの温かいメッセージ

初学者のうちは、「わざわざエラーが出るように制限をかけるなんて、窮屈だな〜」と感じるかもしれません。コードを書く手が止まったり、赤波線にイライラしたりすることもあるでしょう。

でも、安心してください。
TypeScriptがエラーを出してくれるのは、あなたを嫌がらせているわけではありません。「未来のあなたや、一緒に働く仲間がバグで夜中に泣かないように、今のうちに優しく教えてくれている」んです。

データ構造をカチッと固めて、どこをどう変更しても安全な状態を保つこと。これが大規模開発を生き抜くための、現代のフロントエンドエンジニアの大きな武器になります。

まずは怖がらずに、あなたのコードの「絶対に書き換えられたくない大切なデータ」を、ひとつだけ `Readonly` で包んでみてください。きっと、コードの背筋がスッと伸びるような、心地よい安心感に出会えるはずですよ。

それでは、また次の現場でお会いしましょう!Happy Hacking!

コメント

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