TypeScript Partial|効かない罠とRequiredの書き分け
TypeScriptのpartialを使えば、更新処理やフォームで「一部のプロパティだけ持つオブジェクト」を型安全に扱えますが、何でも省略可になるぶん落とし穴も潜んでいます。
Partialは、型のすべてのプロパティを「あってもなくてもよい」状態に一括変換するユーティリティ型です。この記事を読むと、基本の書き方と内部の仕組み、更新処理やフォームでの実用パターン、ネストに効かない罠や型安全が緩む落とし穴への対処、そして対になるRequiredとの書き分けまでを、実際のコードを通して身につけられます。単一プロパティを省略可にする optional(?) との役割分担も整理するので、迷わず使い分けられるようになります。
この記事は次のような方におすすめです。
- 更新処理やフォームでPartialを使い始めた中級者の方
- Partialがネストに効かず困った経験がある方
- 全任意化による型安全の低下が気になっている方
- 対になるRequiredとの使い分けを整理したい方
読み終えるころには、Partialを「いつ使い・いつ避けるか」を判断フローで選べるようになり、緩さを補う書き方まで手に入ります。
それでは、順を追って詳しく見ていきましょう!
- 未経験で後悔したくない
【実体験】未経験からITエンジニアに転職して後悔した話|4社経験してわかった「最初の選択ミス」 - 年収が低くて不安
4年間ずっと年収260万だったエンジニアが、転職で510万になるまでの全記録
Partialとは|全プロパティを任意化するユーティリティ型
Partialは、渡した型のすべてのプロパティをオプショナル(省略可)にした新しい型を作るユーティリティ型です。元の型では必須だったプロパティが、Partialを通すと「あってもなくてもよい」状態になります。部分更新やフォーム入力のように、対象の一部だけを持つオブジェクトを型安全に扱いたい場面で活躍します。
interface User {
id: number;
name: string;
email: string;
}
type PartialUser = Partial<User>;
// 展開結果:
// {
// id?: number;
// name?: string;
// email?: string;
// }
const patch: PartialUser = { name: "Sato" }; // id・emailを省略してもOK
User では3つのプロパティすべてが必須ですが、Partial<User> ではいずれも省略できます。そのため { name: "Sato" } のように一部だけを持つオブジェクトをそのまま代入できます。
ここで混同しやすいのが optional(?) との違いです。単一のプロパティを省略可にするのが ?、型全体のプロパティを一括で省略可にするのがPartialという役割分担になります。name?: string のように特定プロパティだけを任意にしたいなら ? を、既存の型をまるごと任意化したいならPartialを選ぶと考えると整理しやすいですよ。
Partialの基本構文と使用例
書き方は Partial<T> の T に対象の型を渡すだけです。interfaceにもtypeエイリアスにも同じように適用でき、適用後はどちらも全プロパティがオプショナルな型になります。
interface Product {
id: number;
title: string;
}
type Config = {
host: string;
port: number;
};
type PartialProduct = Partial<Product>;
// { id?: number; title?: string; }
type PartialConfig = Partial<Config>;
// { host?: string; port?: number; }
全プロパティが省略可になるため、空オブジェクト {} もPartial型に代入できます。
const empty: Partial<Product> = {}; // エラーにならない
const one: Partial<Product> = { title: "Book" }; // 一部だけでもOK
何も持たない {} が通る点は、Partialの「緩さ」を端的に表しています。この緩さがどんな仕組みから生まれるのかは、内部実装を読み解くと見えてきます。
Partialの実用シーン3選|更新・フォーム・テストデータ
Partialが実務で効くのは、「対象の一部だけを持つオブジェクト」を型として表現したい場面です。代表的なのは次の3つになります。
- オブジェクトの部分更新(既存データに差分をマージ)
- フォーム入力など、値が段階的に埋まっていくデータ
- テスト用モックデータの簡略生成
1つ目の部分更新では、既存データと「変更したいプロパティだけ」を受け取ってマージします。
interface User {
id: number;
name: string;
email: string;
}
function applyPatch(base: User, patch: Partial<User>): User {
return { ...base, ...patch };
}
const current: User = { id: 1, name: "Sato", email: "a@example.com" };
const next = applyPatch(current, { email: "b@example.com" });
// { id: 1, name: "Sato", email: "b@example.com" }
2つ目のフォーム入力では、入力途中の状態を表現できます。未入力のプロパティを省略できるため、段階的に値が埋まる過程をそのまま型で扱えます。
type SignupForm = { name: string; email: string; password: string };
let draft: Partial<SignupForm> = {};
draft = { ...draft, name: "Sato" }; // 名前だけ入力済み
draft = { ...draft, email: "a@ex.com" }; // メールも入力済み
3つ目のテストデータでは、テストごとに必要なプロパティだけを指定したモックを作れます。
function makeUser(overrides: Partial<User> = {}): User {
return { id: 0, name: "test", email: "t@example.com", ...overrides };
}
const u = makeUser({ name: "Mock" }); // nameだけ差し替え
「特定のプロパティだけを抜き出す・取り除く」といった型加工は、PickやOmitが担当します。任意化と組み合わせて型を絞り込みたいときに役立ちます。
部分更新関数でのPartialの典型パターン
部分更新では、Partial<T> を受け取り、スプレッド構文で既存データに重ねるのがよく使われる定番パターンです。受け取り側を Partial<User> にしておくことで、呼び出し側は変更したいプロパティだけを渡せます。
function update(base: User, patch: Partial<User>): User {
return { ...base, ...patch };
}
const before: User = { id: 1, name: "Sato", email: "a@example.com" };
const after = update(before, { name: "Tanaka" });
// { id: 1, name: "Tanaka", email: "a@example.com" }
patch 側のプロパティが後ろのスプレッドで上書きされるため、指定したプロパティだけが差し替わり、残りは元の値が保たれます。また、このマージはトップレベルの浅い結合なので、ネストしたオブジェクトを部分的に更新したい場合は注意が必要です。その挙動は次に整理します。
Partialの内部実装|Mapped Typeとkeyofで読み解く
Partialの定義は標準ライブラリ lib.es5.d.ts に次のように書かれています。
type Partial<T> = {
[P in keyof T]?: T[P];
};
この一行が、全プロパティ任意化の仕組みそのものです。要素を分解すると役割が見えてきます。
| 構文要素 | 役割 |
|---|---|
keyof T |
型 T のプロパティ名(キー)をすべて取り出してユニオンにする |
[P in keyof T] |
取り出した各キー P を順に走査するMapped Type |
? |
走査した各プロパティをオプショナル(省略可)にする修飾子 |
T[P] |
キー P に対応する元の値の型をそのまま引き継ぐ |
つまり「T の全キーを1つずつ取り出し、それぞれに ? を付けて省略可にする」という処理を型レベルで行っているわけです。値の型 T[P] は変えずに、省略可否だけを書き換えている点がポイントになります。この ? 修飾子こそが、空オブジェクト {} すら代入できる緩さの正体です。
定義の先頭にある Partial<T> の T は型引数で、外から渡した型を受け取るための仕組みです。ジェネリクスの基礎を押さえておくと、Mapped Typeの読み解きがぐっと楽になります。
Partialの注意点と落とし穴|効かない場面と型安全
Partialは便利な反面、「緩すぎることで起きる事故」に注意が必要です。代表的な落とし穴は次の2つになります。
1つ目は、ネストしたオブジェクトには浅くしか効かないことです。任意化されるのはトップレベルのプロパティだけで、内側のオブジェクトは必須のまま残ります。
interface Profile {
name: string;
address: { city: string; zip: string };
}
const p: Partial<Profile> = {
address: { city: "Tokyo" }, // 例:zip が無く型エラーになる想定
// (TypeScriptバージョンにより文言が異なる場合あり)
};
address 自体は省略できますが、いざ address を書くと中の zip までは任意化されないため、欠けるとエラーになります。
2つ目は、全任意化によって必須プロパティの欠落を型が検知できなくなることです。本来そろっているべきデータでも、Partialを通した時点で「何も無くてよい」型になります。
function save(user: Partial<User>) {
// id が無くてもコンパイルは通ってしまう
console.log(user.id?.toString());
}
save({}); // 型エラーにならない
このような緩さを抑えるには、任意化したいキーだけを受け付ける更新用パッチなら Partial<Pick<User, "name" | "email">> のように絞ります。ただしPickしなかったプロパティは型から除外されるだけで必須になるわけではありません。任意化しないプロパティを同じ型の中で必須に保ちたいなら Partial<Pick<User, "name" | "email">> & Omit<User, "name" | "email"> のように組み合わせる手もありますが、式が複雑で意図も読み取りづらくなります。ここまで細かく必須と任意を分けたい場面では、専用の interface や type を定義し、任意にしたいプロパティにだけ ? を付けるほうが、どれが必須でどれが任意かが一目で分かり読みやすくなります。
加えて、undefined を明示的に代入できるかどうかは tsconfig の exactOptionalPropertyTypes の設定で変わります。この設定を有効にすると「省略可(プロパティ自体が無い)」と「undefined を代入」が区別されます。無効な場合は Partial<User> に { email: undefined } のような値も渡せてしまい、先ほどの部分更新でスプレッドすると既存値を undefined で上書きするため、必要なら undefined を除外する・入力を検証する・この設定を有効にするといった対策を取ります。
使うべきか避けるべきかは、次のフローで判断するとよいですよ。
- 任意化したいのは型全体か、一部プロパティだけか → 一部だけならPickで絞ってからPartial、全体なら次へ
- 対象はネストを含むか → 含むなら後述のDeepPartialを検討、含まないなら次へ
- 必須欠落を検知できなくても問題ないか → 問題があるなら専用の型を定義して任意にしたいプロパティだけに
?を付ける、またはRequiredやPickで必須範囲を確保。問題なければPartialをそのまま適用
なお、値の書き換え自体を禁止して不変にしたい場合は、任意化とは別の手段で扱います。
ネストしたオブジェクトとDeepPartial
ネストの内側まで丸ごと任意化したいときは、再帰的に自分自身を適用するDeepPartialを自作します。標準のPartialはトップレベルのみなので、深い階層を任意化するには型を再帰させる必要があります。
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
interface Profile {
name: string;
address: { city: string; zip: string };
}
const p: DeepPartial<Profile> = {
address: { city: "Tokyo" }, // zip を省略してもOK
};
値がオブジェクトなら DeepPartial を再帰適用し、そうでなければ元の型を保つことで、ネストした address の中身まで省略可になります。ただし配列やプリミティブが混ざる型では extends object の分岐が意図どおりにならない場合があり、配列要素の扱いなどは個別に調整が必要です(挙動は要確認)。
PartialとRequiredの違いと書き分け
Partialと対になるのがRequiredです。Partialが全プロパティを省略可にするのに対し、Requiredは全プロパティを必須化します。オプショナルだったプロパティから ? を外し、すべてそろっていることを型で強制します。
type Draft = { id?: number; name?: string };
type FullData = Required<Draft>;
// { id: number; name: string; }
const ok: FullData = { id: 1, name: "Sato" };
const ng: FullData = { id: 1 }; // 例:name が無く型エラーになる
// (TypeScriptバージョンにより文言が異なる場合あり)
Partial で緩めた型を Required で締め直す、という往復もできます。
type Loose = Partial<{ a: string; b: string }>; // { a?: string; b?: string }
type Strict = Required<Loose>; // { a: string; b: string }
任意化・必須化・省略可・不変化のどれを選ぶかは、効果と対象範囲で整理すると迷いません。
| 型 | 効果 | 対象範囲 | 対になる型 | 代表用途 |
|---|---|---|---|---|
| Partial | 全プロパティを省略可にする | 型全体 | Required | 部分更新・フォーム |
| Required | 全プロパティを必須化する | 型全体 | Partial | 入力完了データの確定 |
| optional(?) | 単一プロパティを省略可にする | 個別プロパティ | (必須指定) | 一部だけ任意にしたい |
| readonly | プロパティを書き換え不可にする | プロパティの可変性 | (可変) | 不変オブジェクト |
「型全体を一括で任意化/必須化するならPartialとRequired、単一プロパティの省略なら ?、書き換え禁止ならreadonly」と覚えておくと、場面に応じて選び分けられます。
よくある質問
Partialとoptional(?)はどう使い分ける?
単一のプロパティを省略可にしたいなら ?、型全体のプロパティを一括で任意化したいならPartialを使います。1つだけ任意にするのが ?、まるごと任意化するのがPartialという線引きで考えると整理しやすいです。? の詳しい挙動は optional の解説で確認できます。
Partialはネストしたオブジェクトにも効く?
効くのはトップレベルのプロパティだけで、ネストした内側のオブジェクトには浅くしか効きません。深い階層まで任意化したい場合は、再帰的に適用するDeepPartialを自作する必要があります。
Partialの逆(全必須化)はどう書く?
Partialの逆、つまり全プロパティを必須化するには Required<T> を使います。Required はオプショナルなプロパティから ? を外し、すべて必須にするユーティリティ型です。PartialとRequiredの使い分けは書き分けの項で比較表とともに整理しています。
Partialを使うと型安全は下がらない?
全任意化により、本来必須のプロパティが欠落してもエラーにならなくなるため、その分の型安全は緩みます。対策として、任意化したい範囲だけをPickで切り出してPartialを適用したり、確定が必要な箇所にRequiredを併用したりして、緩さの範囲を絞ると安全に保てます。
【付録】さらに学びを深めるためのリソース
さらにTypescriptの学習を進めたい方のために、いくつかのリソースを紹介します。
これらのリソースを活用することで、TypeScriptの型システムについてより深い知識を得ることができるでしょう。
おすすめの書籍
ゼロからわかる TypeScript入門
技術評論社から出版されている「ゼロからわかる TypeScript入門」は、プログラミング初心者や本職プログラマーではない方を主な対象にした入門書です。
変数・条件分岐・ループといった基本から、クラスやインターフェース、モジュールまで段階的に学べる構成になっています。最終章ではWeb APIとJSONを使った非同期Webアプリの作成も体験できるので、「実際に動くものを作る」ところまで到達できます。
プロを目指す人のためのTypeScript入門
技術評論社の「プロを目指す人のためのTypeScript入門 安全なコードの書き方から高度な型の使い方まで」、通称 ブルーベリー本 です。
JavaScriptの仕様とTypeScript独自の機能を両方押さえつつ、リテラル型・ユニオン型・keyof型・ジェネリクスなど、高度な型表現まで踏み込んで解説しています。TypeScriptの型システムの表現力を本格的に学べる一冊です。
オンラインで参照できる公式ドキュメント
TypeScript公式ハンドブック
https://www.typescriptlang.org/docs/
TypeScriptの公式ドキュメントです。
intersection型を含む、すべての型システムの機能について詳細な説明があります。
TypeScript Deep Dive
https://basarat.gitbook.io/typescript/
TypeScriptの深い部分まで掘り下げて解説しているオンラインブックです。
無料で読むことができ、intersection型についても詳しく説明されています。
TypeScriptの学習は終わりがありません。
新しい機能が常に追加され、より良い書き方が発見されています。
継続的に学習を続けることで、より良いTypeScriptプログラマーになれるはずです。
まとめ – Partialは便利だが緩い型と心得て使い分けよう
この記事では、Partialの基本から罠とRequiredとの書き分けまでを整理しました。
- Partialは型の全プロパティを省略可にするユーティリティ型
- 実用シーンは部分更新・フォーム入力・テストデータの3つ
- 内部実装は
{ [P in keyof T]?: T[P] }というMapped Type - 罠は2つ。ネストには浅くしか効かず、必須欠落も検知できない
- 緩さはPickでの絞り込みやRequired併用で補える
- 対になるRequiredは全プロパティを必須化し、書き分けは比較表で判断できる
Partialは「便利だが緩い」型です。何を任意化し、どこは必須に保つかを意識して使い分ければ、更新処理やフォームで安全に活躍してくれます。
※本記事の本文案はAIを活用して作成していますが、記載している内容およびコードは筆者が実際に調査、検証・実行し、内容の正確性を確認した上で公開しています。






