【入門編】 Readonly – TypeScript実践ガイド

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

TypeScriptを触りはじめると、型定義の便利さに感動する一方で、`any`や`unknown`の扱いに戸惑ったり、おなじみのプリミティブ型(`string`や`number`など)の組み合わせで頭が痛くなったりしますよね。

「せっかく作ったオブジェクトのプロパティを、うっかり書き換えてしまってバグを生んだ……」
「APIから取ってきた大切なデータを、どこかで誰かに勝手に書き換えられないようにガードしたい!」

現場でバリバリ開発している私たちプロでも、油断するとこういうヒューマンエラーはやらかします。そんなときにそっと寄り添って守ってくれる、めちゃくちゃ頼れる相棒がいます。それが今回ご紹介する `Readonly` というユーティリティ型です。

難しい専門用語はなるべく置いておいて、身近な例えと一緒にゆっくり紐解いていきましょう。大丈夫、一緒に見ていけばすぐに使いこなせるようになりますよ!

—

1. `Readonly` ってなに?(身近な例えで考えてみる)

いきなりコードを書く前に、まずはイメージを掴みましょう。

想像してみてください。あなたは今、美術館の館長さんです。
展示ケースの中に、世界に一つだけの素晴らしい絵画(データ)を飾りました。この絵画は、来場客に見せるためのものであり、勝手に絵の具を上から塗り足されたり、別の絵に描き変えられたりしては困りますよね。

つまり、「見ることはできる(Read)」けれど、「書き換えることはできない(Only)」状態です。

TypeScriptの `Readonly` は、まさにこれと同じことをやってくれます。普通のオブジェクト(書き換え放題のキャンバス)を渡すと、すべてのプロパティに「お手を触れないでください」という目に見えないプラカードを立てて、読み取り専用の安全な宝箱に変えてくれる魔法の型なんです。

—

2. 基本のキ:普通のオブジェクトとの違い

まずは、TypeScriptの基本的な型(`string`や`number`など)で構成されたオブジェクトを例に、うっかりミスが起きる瞬間と、それを `Readonly` がどう防いでくれるのかを見てみましょう。

以下のコードを、あなたのエディタ(Playgroundなど)に貼り付けてみてください。

// ユーザーのプロフィール情報を表す型です
type User = {
name: string;
age: number;
};

// 普通のオブジェクトを作ります
const pablo: User = {
name: “パブロ”,
age: 28,
};

// 【ふつうの状態】
// 後から名前や年齢を書き換えることが「できてしまいます」
pablo.name = “ピカソ”; // おっと、名前が勝手に変わっちゃった!
pablo.age = 30; // 年齢も書き換え可能

これ、一見すると柔軟で便利に見えますが、アプリの規模が大きくなると「えっ、一体どこでこの `name` を書き換えたの!?」という迷宮入りバグの原因になります。

そこで登場するのが、私たちの主役 `Readonly` です!

// Readonlyを使って、絶対に書き換えられないユーザー型を作ります
type ReadonlyUser = Readonly;

const safePablo: ReadonlyUser = {
name: “パブロ”,
age: 28,
};

// 【Readonlyで守られた状態】
// safePablo.name = “ピカソ”;
// ↑ ここでTypeScriptが赤く波線を出して怒ってくれます!
// 「エラー: 読み取り専用プロパティであるため、’name’ に値を代入することはできません。」

おぉ、すごい!たった `Readonly<...>` で囲うだけで、TypeScriptのコンパイラが「ここは触っちゃダメだよ!」とボディブローのようにガッチリガードしてくれました。これなら、うっかりミスを未然に防げますね。

—

3. 実務でよくあるつまずきポイント & 優しい処方箋

「じゃあ、すべての型をとりあえず `Readonly` にすれば最強なんじゃ?」と思いますよね。ここで、現場の初学者がよくつまずくポイントをそっとシェアしておきます。

つまずきポイント:部分的に書き換えたいときはどうするの?

「オブジェクト全体を `Readonly` にしちゃうと、一部のステータスだけ変えたいときに困るよ……」という声が聞こえてきそうです。

大丈夫、そんなときはTypeScriptの「部分型」や「必要なところだけ型をつける」というアプローチを使います。

type Todo = {
id: number;
title: string;
completed: boolean;
};

// IDとタイトルは変えられたくないけど、完了状態(completed)だけは切り替えたい!
// そんなときは、Readonlyにするプロパティを個別に指定することもできます。
type StrictTodo = {
readonly id: number; // IDは絶対に死守する
readonly title: string; // タイトルも変えさせない
completed: boolean; // 完了・未完了の切り替えだけは自由にしてOK!
};

const myTodo: StrictTodo = {
id: 1,
title: “TypeScriptの型を極める”,
completed: false,
};

// これはOK(completedはReadonlyにしていないから)
myTodo.completed = true;

// myTodo.title = “JavaScriptを極める”; // ← これはエラーで怒られます!

このように、「どこを絶対に守りたくて、どこを動的に変えたいのか」を設計の段階で考えてあげるのが、ワンランク上のフロントエンドエンジニアへの第一歩です。

—

4. 配列(Array)やタプルでも大活躍!

`Readonly` は、オブジェクトだけではなく、配列(`string[]` や `number[]` など)やタプル型でも使えます。

実は、JavaScriptの配列には `push` や `pop` といった「中身を破壊的に書き換えるメソッド」がたくさんあります。意図しないタイミングで配列が書き換えられるのを防ぐために、ここでも `Readonly` が使えます。

// 数値の配列を読み取り専用にする
type ScoreList = Readonly;

const scores: ScoreList = [80, 90, 100];

// scores.push(70);
// ↑ エラー! ‘push’ プロパティは読み取り専用型には存在しません、と怒られます。
// 要素を追加したり、並び替えたりすることができなくなるため、安全にデータを保持できます。

「データを安全に受け取って、画面に表示するだけ」というシーン(例えば、Reactのコンポーネントにpropsとしてデータを渡すときなど)では、この `Readonly` な配列やオブジェクトが大活躍します。

—

おわりに:少しずつ、自分のペースで大丈夫

今回は、TypeScriptの `Readonly` について、身近な例えを交えてお話ししましたがいかがでしたでしょうか?

最初は「なんだかルールが増えて面倒だな」と感じるかもしれません。でも、この型による制約は、未来のあなたや、一緒に働くチームの仲間を救うための「優しいお守り」なんです。

エラーが出たら、「あ、TypeScriptが教えてくれてラッキー!」と笑顔で受け止めながら、少しずつコードを書いていってくださいね。あなたのTypeScriptライフが、より楽しく快適なものになるよう、チーフアーキテクトとしていつも応援しています!

コメント

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