【入門編】 基本ユーティリティ型 (Partial, Required, Readonly) – TypeScript実践ガイド

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

新しい技術を学び始めるとき、「型定義なんて難しそう…」「エラーばかり出て心が折れそう…」そんなふうに身構えてしまうこと、ありますよね。大丈夫ですよ、最初はみんなそこでつまずきます。型はあなたを縛り付けるための窮屈な檻ではなく、あなたのコードを守ってくれる「頼もしい相棒」なんです。

今回は、TypeScriptの標準ユーティリティ型の中から、オブジェクトの見た目をガラリと変えてしまう`Partial`、`Required`、`Readonly`の3つを取り上げます。

実務の現場でも毎日のように顔を出す超重要メンバーたちです。身近な例えを交えながら、優しく紐解いていきましょう!

—

ユーティリティ型って、なに?

難しそうな名前がついていますが、要するに「既存の型をベースにして、ちょっとだけ便利な形にカスタマイズしてくれる魔法の道具箱」だと思ってください。

一から型を書き直すのは面倒ですよね。「今の型をベースに、全部のプロパティを任意(あってもなくてもいい状態)にしたい!」そんなわがままをスマートに叶えてくれるのが、これらのお仕事です。

—

1. `Partial` —— 「ぜんぶ、なくてもOKだよ」の優しさ

身近な例え:ネットショップの「会員登録情報の変更フォーム」

想像してみてください。プロフィールを編集するとき、名前だけ変えたいときもあれば、住所だけ変えたいときもありますよね。「すべての項目を毎回必ず入力しなさい!」と言われたら、ユーザーはイライラしてページを閉じちゃうでしょう?

TypeScriptの通常のオブジェクト型は、原則として「定義されたプロパティはすべて必須」です。でも、データの一部だけを更新(アップデート)したいときには、この厳格さが逆に仇になります。

そこで登場するのが `Partial` です。型が持つすべてのプロパティに「なくてもいいよ(オプショナル)」という`?`の魔法をかけてくれます。

コードで見てみましょう

// 1. 基本のユーザー型(名前も住所も必須!)
interface User {
name: string;
email: string;
age: number;
}

// 2. データベースを更新する関数を考えてみます
// 「名前だけ」「メールだけ」更新したいこともあるよね…
function updateProfile(userId: string, updatedData: Partial) {
// Partialのおかげで、updatedDataの中身はすべて「あってもなくてもOK」になります!
console.log(`ユーザー ${userId} を更新します`, updatedData);
}

// 使い方:メールアドレスだけ変えたい!
updateProfile(“user_001”, {
email: “new-email@example.com”,
// nameもageも書かなくても、TypeScript怒らないよ!優しいね!
});

「全部揃っていなくても怒らないでね」という、現場で一番よく使う愛されユーティリティ型です。

—

2. `Required` —— 「ぜんぶ、絶対に入力してね!」の厳格さ

身近な例え:ジェットコースターの「安全バー」

先ほどの`Partial`とは真逆の世界です。世の中には、「一部が欠けていると大惨事になる」データも存在します。例えば、ゲームの最終スコア送信データや、絶対に空欄が許されない契約書の同意フラグなどです。

`Required` は、たとえ元の型で「あってもなくてもいいよ(`?`付き)」となっていたプロパティであっても、「いや、やっぱり全部必須にして!」と強制的に強制力を高める、ちょっぴりスパルタな道具です。

コードで見てみましょう

// 設定を表す型(最初は何も設定しなくてもいいように「?」がついている)
interface GameSettings {
volume?: number;
difficulty?: string;
soundEnabled?: boolean;
}

// ゲームを実際にスタートさせる関数
// ここでは、設定の抜け漏れは絶対に許されない!
function startGame(settings: Required) {
// Requiredのおかげで、volumeもdifficultyもsoundEnabledも、
// すべて「必ず存在する(undefinedではない)」と安心して処理できる!
console.log(`ゲームスタート! 音量: ${settings.volume}`);
}

// ❌ エラーになる例(設定が足りないよ!)
// startGame({
// volume: 80,
// // difficulty と soundEnabled が足りないからTypeScriptが怒る!
// });

// ⭕️ 正しい使い方(全部揃える)
startGame({
volume: 80,
difficulty: “Normal”,
soundEnabled: true,
});

「うっかり設定し忘れた!」というヒューマンエラーを、コンパイルの段階でバシッと防いでくれる心強い存在です。

—

3. `Readonly` —— 「見るだけ! 書き換え禁止!」のガードマン

身近な例え:美術館の「お手を触れないでください」の看板

プログラムを書いていて、「このデータは一度作ったら、絶対に中身を書き換えられたくない(イミュータブルに扱いたい)」という場面に出会います。例えば、設定ファイルの定数や、ID、一度確定した注文履歴などです。

通常、TypeScriptのオブジェクトは後から`user.name = “別の名前”`と簡単に書き換えられてしまいます。しかし、`Readonly` で包み込むと、すべてのプロパティに「読取専用(readonly)」の鍵がかかり、書き換えようとした瞬間にTypeScriptが赤色の波線で警告を出してくれます。

コードで見てみましょう

// ログイン中のユーザー情報(IDやメールアドレスは勝手に書き換えられた困る!)
interface AuthUser {
readonly id: string; // ここにもreadonlyがついているけど
name: string;
}

// アプリケーション全体で共有する設定オブジェクト
interface AppConfig {
readonly apiUrl: string;
readonly timeout: number;
}

// Readonlyを使って、設定オブジェクト全体を保護する!
const config: Readonly = {
apiUrl: “https://api.example.com”,
timeout: 5000,
};

// ❌ エラーになる例(書き換えようとすると怒られる!)
// config.timeout = 10000;
// ⚠️ エラー: Cannot assign to ‘timeout’ because it is a read-only property.

// 読む分にはもちろん大歓迎!
console.log(`接続先: ${config.apiUrl}`);

「意図しないバグ(気づかないうちに変数を上書きしちゃった事件)」を防ぐための、最高のガードマンです。実務では、Reduxのステート管理やAPIレスポンスの型定義などで本当によくお世話になります。

—

チーフアーキテクトからのまとめ

いかがでしたでしょうか? 今回紹介した3つのユーティリティ型は、どれもベースとなる型を「ちょこっと加工する」ためのものです。

  • `Partial`: 「ぜんぶ、なくてもOK(オプショナル)」にする優しさ
  • `Required`: 「ぜんぶ、絶対必須!」にする厳格さ
  • `Readonly`: 「見るだけ! 書き換え禁止!」にするガードマン

これらを使いこなせるようになると、わざわざ同じような型を何個も手書きで定義し直す必要がなくなります。「DRY原則(Don’t Repeat Yourself:同じことを何度も書かない)」をTypeScriptの型レベルで美しく実現できるんです。

最初は覚えることが多いように感じるかもしれませんが、日々の開発の中で「あ、ここは書き換えられたくないからReadonlyにしよう」と自然に手が動くようになると、TypeScriptを書くのがぐっと楽しくなりますよ。

もしエラーが出ても、それはTypeScriptが「もっとこうした方が安全だよ」と優しく教えてくれているサインです。焦らず、一歩ずつ進んでいきましょう。応援しています!

コメント

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