みなさんこんにちは!ヒロポンです!!
「来週から TypeScript の案件、ちょっと入ってもらうかも」
C# を長くやってると、こういう話がある日ふっと降ってくる。で、腹をくくって触ってみると、文法はだいたい読めるんですよね。
読める。読めるんだけど、Partial<User> とか Pick<User, "id"> みたいな 型から型を作る やつ。あれを見た瞬間、「え、これ C# のどれに当たるん??」って手が止まる。
理由はシンプルで、C# にはそのものズバリの言語機能が無いからなんです。だから既知の引き出しに紐づかない。紐づかないから、必要以上に身構えてしまう。
今日は、C# 出身者向けに TypeScript ユーティリティ型(Partial / Pick / Omit / Record)を、「C# でいうアレ」に対応づけて整理します。ゴールは、TypeScript ユーティリティ型を C# の感覚のまま読めるようにすること。それだけです。
💡 型そのものの違い(構造的型付け)は TypeScript の interface と C# の interface は別物 にまとめてあります。本シリーズと同じ「C# 出身者の橋渡し」目線で並べているので、合わせて読むと入りやすいはず。
C# だと「既存の型から一部だけ使う型」を作るのが地味に面倒
C# で「User クラスの一部のプロパティだけ持つ型」が欲しくなったこと、ありますよね。更新用に一部だけ。一覧表示用に id と name だけ。そういうやつ。
C# だと専用のクラス(DTO)を別に定義するか、リフレクションでこねるか、の二択になる。
俺も昔、更新用・一覧用・詳細用で似たようなクラスを3つ手書きして、プロパティを1個足すたびに3箇所直す羽目になったことがあります。あれ、地味にだるいんですよね。
TypeScript は、この「既存の型から新しい型を組み立てる」を 型レベルの1行 で片づける。それがユーティリティ型です。
対応マップ: C# のやり方 ↔ TS ユーティリティ型
先に全体像から。C# で手書きしてたやつが、TS だとどのユーティリティ型に化けるか。対応で並べます。

ここから、この共通の型をベースに1個ずつ見ていきます。まずは土台になる User 型。
interface User {
id: number;
name: string;
email: string;
role: "admin" | "member";
}
対応1: Partial — 全プロパティを任意にする
Partial<User> は、User の全プロパティを「任意(? 付き)」にした型を返す。C# でいうと、更新用に全プロパティを nullable にした別クラスを手書きするやつ。あれが1行で済みます。
// Partial<User> = { id?: number; name?: string; email?: string; role?: "admin" | "member" }
type UserPatch = Partial<User>;
function updateUser(id: number, patch: UserPatch): void {
// 一部だけ渡せるので、更新処理にちょうどいい
console.log(`update ${id}:`, patch);
}
updateUser(1, { name: "佐藤" }); // name だけ更新
updateUser(1, { email: "sato@example.com" }); // email だけ更新
updateUser に「変えたいプロパティだけ」を渡せる。こんな感じで、部分更新の引数型としてバチッとハマります。地味だけど効くやつです!!ただ、この「全部が任意」という性質。後でしっかり刺さってくるので、頭の隅に置いといてください。
対応2: Pick<T, K> — 必要なプロパティだけ抜く
Pick<User, "id" | "name"> は、User から id と name だけ を抜き出した型。一覧表示用の DTO を別に書いてたやつが、これに置き換わります。
// 一覧表示に必要な id と name だけ
type UserSummary = Pick<User, "id" | "name">;
const summary: UserSummary = { id: 1, name: "佐藤" };
// email や role を足すと「その型に存在しない」でコンパイルエラーになる
うれしいのは、元の User に項目が増えても UserSummary は id と name のままな点。C# で DTO を手書きしてると元クラスに引きずられて直し忘れが出るんだけど、Pick は「明示した分だけ」なのでズレない。
対応3: Omit<T, K> — いらないプロパティを除く
Omit は Pick の逆で、「これ以外ぜんぶ」を作ります。Omit<User, "id"> なら、id を除いた残り全部。
// 新規作成時は id をまだ持っていない → id を除いた型
type NewUser = Omit<User, "id">;
const draft: NewUser = {
name: "鈴木",
email: "suzuki@example.com",
role: "member",
};
// draft に id を書くと「存在しない」でエラー。逆に name を抜くと「必須が足りない」でエラー
使い分けはシンプルで、「除きたいものが少ないなら Omit、残したいものが少ないなら Pick」。これで読みやすくなります。どっちも元の型に自動で追従してくれる。
対応4: Record<K, V> — キーと値の型付き辞書
Record<K, V> は、キーの型 K と値の型 V を指定した辞書型。C# の Dictionary<K, V> に近いんだけど、キーを網羅チェックしてくれる のが強い。
// ロールごとの表示ラベルを、キー漏れなく定義する
type RoleLabel = Record<User["role"], string>;
const labels: RoleLabel = {
admin: "管理者",
member: "メンバー",
};
// "admin" か "member" のどちらかを書き忘れると「不足してる」でエラーになる
User["role"] は "admin" | "member" を指す書き方。これを Record のキーに据えると、ロールが3つ目に増えたとき「labels にそのキーが無い」とコンパイルが教えてくれる。いい感じに安全側へ倒せます。Dictionary だとキー漏れは実行時までバレない。だからここは、TS の型が地味に効くところ。
ハマりポイント: Partial を安易に使うと必須が抜けても通る
さっき「後で刺さる」と言った Partial の話。ここです。
Partial<T> は 全プロパティを任意にする。更新には最高なんですが、新規作成に使うと事故る。必須のはずのプロパティが抜けても、コンパイルが通ってしまうんですよ。

before は、Partial<User> を作成関数に使ってるせいで、name を渡し忘れても素通りする。で、実行時に name が空文字のユーザーが静かに作られて、後の画面で「あれ、なんで名前が空欄なん??」ってなってから初めて気づく。俺はこれで小一時間溶かしました。
after のほうは、作成を Omit<User, "id">(id 以外は必須のまま)にしてある。こうすると、渡し忘れたその場でコンパイルエラーになる。「Partial は更新、作成には使わない」——覚えるのはここだけ。それで、この事故は踏みません。
俺の現場メモ: キャッチアップで効いたコツ
C# から TS に入るとき、俺が一番効いたのは「新しい記法を覚える、と思わない」ことでした。
Partial も Pick も、やってることは C# で DTO を手で書いてた作業と同じ。「あの手書きが1行になっただけ」と捉えると、急に身近になるんですよね。名前がゴツいだけで、発想は前からやってたこと。
もう1つ。ユーティリティ型は 組み合わせられる。Partial<Pick<User, "name" | "email">> みたいに、Pick で絞ってから Partial で任意化、とか。とはいえ最初から欲張らないほうがいい。まず Partial / Pick / Omit / Record の4つを単体で手に馴染ませる。そうすると、いい感じに肩の力が抜けます。俺はこれが近道でした。
まとめ
- TS のユーティリティ型は「既存の型から新しい型を作る」機能。C# で DTO を手書きしてたやつの代わり
Partial<T>(全部任意)/Pick<T, K>(一部を抜く)/Omit<T, K>(一部を除く)/Record<K, V>(型付き辞書)Recordはキー網羅チェックが効くのでDictionaryより事故りにくいPartialは更新に使う。新規作成に使うと必須が抜けても通るのでOmitで必須を保つ
C# の「手書きしてた面倒」を思い出しながら読むと、ユーティリティ型は一気に手に馴染む。既知の作業に紐づける。これがキャッチアップの最短ルートです。
よくある質問
Q1. TypeScript のユーティリティ型は C# でいうと何にあたりますか?
ちょうど対応する言語機能は C# にはありません。C# では更新用・一覧用に DTO クラスを別途手書きするか、ジェネリクスやリフレクションで組みますが、TypeScript は Partial / Pick / Omit / Record といった型レベルの合成で、既存の型から新しい型を1行で作れます。
Q2. Partial を使うときの注意点は?
Partial<T> は T の全プロパティを任意(省略可)にします。更新処理には便利ですが、新規作成に使うと必須のはずのプロパティが抜けてもコンパイルエラーにならず、実行時に空の値が入る事故につながります。作成には Omit<T, "id"> のように必須を保った型を使い分けます。
Q3. Pick と Omit はどちらを使えばいいですか?
残したいプロパティが少ないなら Pick<T, K>、除きたいプロパティが少ないなら Omit<T, K> が読みやすいです。どちらも元の型 T が変わると自動で追従するので、DTO を手書きするより保守が楽になります。
Q4. Record<K, V> と Dictionary の違いは?
どちらもキーと値の対応ですが、Record<K, V> はキーの型をユニオン(例: "admin" | "member")で縛れて、キーの書き漏れをコンパイル時に検出できます。Dictionary は実行時までキー漏れが分からないので、決まったキー集合を扱うなら Record が安全です。
Q5. ユーティリティ型は組み合わせられますか?
はい。Partial<Pick<User, "name" | "email">> のように入れ子にできます。まずは4つを単体で使えるようにして、慣れてから組み合わせに進むのがおすすめです。
次に読むべき記事





以上!
執筆者
バイブス父さん — 業務 SE 7 年 (SIer 正社員 2 / フリーランス 5)。 現職は SEO 直轄部の AI アドバイザー兼 PL、 副業で中小 SIer の CTO。 SIer の正社員からフリーランスに転じ、 複数のエージェント経由で案件を回してきた経験ベースで「業務 SE 視点」 の技術 + キャリア記事を書いています。
🐦 X: @hiro_progra0524 (日々の現場メモ更新中)
📝 About Me で経歴詳細を見る



