こんにちは!フロントエンドの現場を渡り歩いているチーフアーキテクトの私です。
TypeScriptを書き始めたばかりの頃って、次から次へと出てくる見慣れない記号や謎の単語に圧倒されちゃいますよね。「`readonly`?」「マップ型?」「なんだか難しそう……」と、エディタを開く手が思わず止まってしまう気持ち、痛いほどよく分かります。
大丈夫、安心してください。今日お話しする「Readonly修飾子付きマップ型」なんて、名前はものすごく厳めしいですが、中身を知ってしまえば「なーんだ、そういうことね!」と肩の力が抜ける便利なテクニックなんです。
身近な例えを交えながら、ゆっくり紐解いていきましょうか。コーヒーでも飲みながら、気楽に聞いていってくださいね。
—
そもそも「マップ型」ってなぁに?(お弁当箱の例え)
TypeScriptにおける「マップ型(Mapped Types)」をひとことで言うと、「既存の型をベースにして、新しい型を効率よくコピー&アレンジする仕組み」です。
たとえば、あなたがお弁当屋さんを営んでいるとしましょう。
そこには「お弁当のメニュー表」があります。
- おにぎり(値段:150円)
- から揚げ(値段:300円)
「このメニュー表の項目を、ぜんぶ『売り切れ(変更不可)』に変えたいな」と思ったとき、わざわざメニューを1から書き直すのは面倒ですよね。
そんなとき、既存のメニュー表をコピーしつつ、「一括で変更できないように鍵をかける」ことができるのが、TypeScriptのマップ型なのです。
—
主役の登場:`readonly` という名の「鍵」
TypeScriptの `readonly` は、オブジェクトのプロパティに「おい、あとから勝手に書き換えるなよ!」と鍵をかける修飾子です。
// 普通のメモ帳(あとから自由に書き換えられる)
type Memo = {
content: string;
};
const myMemo: Memo = { content: “牛乳を買う” };
myMemo.content = “卵を買う”; // 上書きOK!
// 鍵付きのメモ帳(一度書いたら書き換えられない)
type SecureMemo = {
readonly content: string;
};
const mySecureMemo: SecureMemo = { content: “絶対に忘れないパスワード” };
mySecureMemo.content = “変えちゃおっと”; // ❌ エラー!「読み取り専用だから書き換えちゃダメだよ!」と怒られます
この `readonly` を、プロパティを1つずつ手作業でつけていくのではなく、マップ型と組み合わせて一気にドバッと付与・剥奪するのが今回のテーマです。
—
1. プロパティに「読み取り専用(readonly)」の鍵をかける
既存の型にあるすべてのプロパティに対して、一気に `readonly` を付けたいときは、マップ型の構文の中で `readonly` を先頭にくっつけます。
さっそく、エディタに貼り付けて試せるコードを見てみましょう。
/
- ユーザーのプロフィール情報を表す型
/
type UserProfile = {
name: string;
age: number;
};
/
- 【マップ型】
- 渡された型(T)のすべてのプロパティに「readonly(鍵)」を付与する型
/
type ReadonlyProfile
readonly [K in keyof T]: T[K];
};
// 使い方
type LockedUserProfile = ReadonlyProfile
// 実践!
const user: LockedUserProfile = {
name: “田中さん”,
age: 28,
};
// 試しに名前を変えてみようとすると……?
// user.name = “佐藤さん”;
// ❌ ここでTypeScript先生が「readonlyプロパティだから代入できません!」と赤く波線を引いて止めてくれます。
ここで使っている `[K in keyof T]` という見慣れない記号は、「Tという型の中にあるプロパティをひとつずつ(Kとして)ぜんぶ取り出してループするよ!」という、いわばプロパティの全自動コピー機のようなものです。その先頭に `readonly` を添えるだけで、コピーされたすべてのプロパティが「読み取り専用」に変身します。
—
2. 反対に、鍵を「外す(`-readonly`)」こともできるんです
「じゃあ、逆に全部 `readonly` がついているガチガチの型から、鍵を外して自由に書き換えられるようにしたいときはどうするの?」
そんなワガママにも、TypeScriptはちゃんと応えてくれます。
鍵を外すときは、マイナスの記号をつけて `-readonly` と書きます。
/
- 最初からガチガチに鍵がかかっている設定型
/
type AppConfig = {
readonly apiEndpoint: string;
readonly timeout: number;
};
/
- 【マップ型】
- 渡された型(T)の「readonly」を強制的に剥ぎ取る型
/
type MutableConfig
-readonly [K in keyof T]: T[K];
};
// 使い方:ガチガチの型から、鍵を外して自由の身にする!
type EditableConfig = MutableConfig
// 実践!
const config: EditableConfig = {
apiEndpoint: “https://api.example.com”,
timeout: 5000,
};
// 鍵が外れているので、後から値を書き換えても怒られません!
config.timeout = 10000; // ⭕️ OK!スイスイ書き換えられます
この `-readonly` の `-` は、「マイナス(取り除く)」という意味です。
「あ、このプロパティについてる鍵、不要だから取っちゃお!」というときの魔法の合言葉ですね。
—
実務の現場では、標準の便利ユーティリティ型も使おう
ここまで「マップ型で `readonly` を付け外しする方法」を自作のコードで解説してきましたが、実はTypeScriptには、わざわざ自分でこのマップ型を書かなくてもいいように、最初から用意されている標準の便利ツール(ユーティリティ型)があります。
実務では、これらをサクッと使うことのほうが圧倒的に多いです。
1. `Readonly
- 先ほど自作した「全部に鍵をかけるマップ型」の公式バージョンです。
- 例:`type Locked = Readonly
;`
2. `Mutable
- 鍵を外す `-readonly` 型は、実務でもよく自作してプロジェクト内の便利フォルダ(`types/utils.ts` など)に置いて重宝されています。
—
まとめ:怖がらなくて大丈夫、型はただの「設計図」
ここまでお疲れ様でした!
「Readonly修飾子付きマップ型」について、少しはモヤが晴れてすっきりしたでしょうか?
- マップ型 は、既存の型を全自動でコピー&アレンジする仕組み。
- `readonly` は、プロパティに「書き換え禁止」の鍵をかける。
- `-readonly` は、その鍵をガチャッと外す。
TypeScriptの型定義は、突き詰めると「自分やチームの仲間が、将来うっかりバグを踏まないようにするための優しいお守り(設計図)」です。最初は呪文のように見えても、こうして身近なものに置き換えてみると、エンジニアの悩みを解決するすごくロジカルで優しい道具だということが分かりますよね。
もしまた分からない記号や構文に出会ったら、いつでもこのブログに帰ってきてください。一歩ずつ、あなたのペースで楽しくTypeScriptとお友達になっていきましょう!それでは、また次の現場でお会いしましょうー!

コメント