こんにちは。現場で日々TypeScriptの型パズルと格闘している君なら、一度や二度はお世話になっているはずだ。そう、`Partial
型 `T` のすべてのプロパティをごっそりオプショナル(省略可能)にしてくれる、あの便利なユーティリティ型だ。
「APIから返ってくるデータの一部だけを更新するパッチリクエスト(PATCH)の型定義に最高!」
「フォームの初期値を定義する時に便利!」
……と、ここまでは入門書や公式ドキュメントに書いてある通り。しかし、実務の現場では「なんとなく便利だから」と雑に使ってしまい、後々「あれ、このプロパティ、本当は必須のはずなのにundefinedが混ざってランタイムエラーになったぞ……?」というバグを踏み抜く中級エンジニアの姿を本当によく見かける。
今回は、この `Partial
—
1. `Partial` の基本仕様と「裏側」の仕組み
まずは基本のおさらいだ。`Partial
type Partial
[P in keyof T]?: T[P];
};
たったこれだけのコードだが、やっていることは非常に強力だ。
1. `keyof T` で型 `T` のすべてのプロパティ名(キー)をユニオン型として抽出する。
2. `in` 演算子でそのキーをループ(マップ)する。
3. `?` を付与することで、すべてのプロパティをオプショナル(`undefined` を許容する状態)に書き換える。
⚠️ ブラウザ(ランタイム)の裏側はどうなっているのか?
ここでエンジニアとして一歩踏み込んで考えてほしい。この `Partial
答えは「何の影響も与えない」だ。
TypeScriptの型はすべて、ビルド時(コンパイル時)に綺麗に消し去られる(Erasure)。ブラウザが動かすのは、純粋なJavaScriptのコードだけだ。
つまり、`Partial
型定義はあくまで「開発中のIDEの補完」と「コンパイル時の型チェック」のためのもの。この大前提を忘れると、「型エラーが出ないから大丈夫」と油断して、実行時エラーの爆弾を抱えることになる。
—
2. 現場で即コピペできる!実践的なコード例
では、実際のフロントエンド開発で `Partial
/
- ユーザーの基本情報(データベースの完全なレコードを想定)
/
interface UserProfile {
id: string;
username: string;
email: string;
bio: string;
age: number;
}
/
- 【NG例】雑に全体を Partial にしてしまうケース
- これだと、絶対に書き換えてほしくない ‘id’ までオプショナルになり、
- さらに何を更新しようとしているのか意図が曖昧になる。
/
// type BadUpdateUserDto = Partial
/
- 【Good例】Omit と Partial を組み合わせた堅牢なアプローチ
- 1. 変更不可な ‘id’ は Omit で除外する
- 2. 残りのプロパティを Partial でオプショナルにする
/
type UpdateUserProfilePayload = Partial
// — 実装イメージ —
/
- ユーザープロフィールを更新するAPIクライアント関数
- @param userId 更新対象のユーザーID(識別子は必ず別途必須として持たせる)
- @param payload 変更したいフィールドだけを詰めたオブジェクト
/
async function updateUserProfile(
userId: string,
payload: UpdateUserProfilePayload
): Promise
// 実際のフェッチ処理
const response = await fetch(`/api/users/${userId}`, {
method: ‘PATCH’,
headers: { ‘Content-Type’: ‘application/json’ },
body: JSON.stringify(payload),
});
if (!response.ok) {
throw new Error(‘プロフィールの更新に失敗しました’);
}
return response.json();
}
// — 使用例 —
// OK: メールアドレスと自己紹介だけを更新する場合
const patchData: UpdateUserProfilePayload = {
email: ‘new-ishiki@example.com’,
bio: ‘TypeScriptを愛するフロントエンドエンジニアです。’,
};
updateUserProfile(‘user_12345’, patchData);
このパターンの何が素晴らしいのか?
1. 変更してはいけない主キー(`id`)を型レベルで保護している
`Partial
2. APIの仕様(PATCHリクエスト)と型が美しく1対1で対応する
バックエンドの「ID指定はパスパラメータで行い、リクエストボディには変更分だけを乗せる」というRESTの設計思想に、TypeScriptの型が完璧に寄り添っている。
—
3. シニアが教える「`Partial` の罠」と回避策
最後に、実務で多くのエンジニアがハマる `Partial
罠:ネストしたオブジェクト(Deep Partial)問題
`Partial
interface AppConfig {
theme: {
mode: ‘light’ | ‘dark’;
accentColor: string;
};
pagination: {
limit: number;
offset: number;
};
}
// Partial
// {
// theme?: { mode: ‘light’ | ‘dark’; accentColor: string; }; // ← theme 自体は省略可能だが、中の mode は省略不可!
// pagination?: { limit: number; offset: number; };
// }
「設定の一部だけを渡したいから `Partial
💡 解決策:DeepPartial を自作する(または既存のライブラリを使う)
階層の深いオブジェクトのすべてのプロパティを再帰的にオプショナルにしたい場合は、次のような再帰型(Recursive Type)を定義するのが、シニアの現場の常道だ。
/
- オブジェクトのネスト構造の奥底まですべてオプショナルにするユーティリティ型
/
type DeepPartial
[P in keyof T]?: T[P] extends object
? DeepPartial
: T[P];
};
// これを使えば、以下のようにネストした一部だけを安全に渡せるようになる
const myConfig: DeepPartial
theme: {
mode: ‘dark’, // accentColor を書かなくてもエラーにならない!
},
};
(※実務では、`utility-types` などの信頼できるサードパーティライブラリからインポートして使うことも多い)
—
まとめ
`Partial
- ただの思考停止で `Partial
` を使うな。 `Omit` や `Pick` と組み合わせて、本当にオプショナルであるべき範囲を絞り込もう。 - ネスト構造には要注意。 深い階層のデータには再帰的なアプローチ(`DeepPartial`)を検討しよう。
- 型はランタイムを救わない。 型でオプショナルにしても、実行時のバリデーション(ZodやValibotなど)と組み合わせることで初めて本当の意味で堅牢なアプリケーションになることを忘れないでほしい。
今日のこの知識を武器に、君の書くTypeScriptコードをさらに一歩、洗練されたものにしてくれ。応援しているぞ!

コメント