TypeScript のユーティリティ型
TypeScript には十数種類のユーティリティ型が用意されています。これらはジェネリックプログラミングにおけるベストプラクティスを体現しており、1 行のコードで一般的な型変換を実行できるようにします。
1. プロパティ変換クラス
(1) 部分的 — すべてのプロパティがオプションになる
TYPESCRIPT
interface User {
id: number;
name: string;
email: string;
age: number;
}
// Partial Make all properties optional——Suitable for update operations
type PartialUser = Partial<User>;
// { id?: number; name?: string; email?: string; age?: number }
function updateUser(user: User, updates: Partial<User>): User {
return { ...user, ...updates };
}
let user: User = { id: 1, name: "Charlie", email: "xiao@example.com", age: 20 };
let updated = updateUser(user, { age: 21 });
// Update Only age,All other attributes remain unchanged
console.log(updated.age); // 21
console.log(updated.name); // "Charlie"(Unchanged)
(2) 必須—すべてのプロパティが必須となりました
TYPESCRIPT
interface Config {
host?: string;
port?: number;
debug?: boolean;
}
// Required Make all fields required——Suitable for verifying logic
type RequiredConfig = Required<Config>;
// { host: string; port: number; debug: boolean }
function validateConfig(config: RequiredConfig): void {
console.log(`${config.host}:${config.port} (debug: ${config.debug})`);
}
(3) 読み取り専用—すべてのプロパティが読み取り専用になります
TYPESCRIPT
interface Point {
x: number;
y: number;
}
// Readonly Make all properties read-only
type ReadonlyPoint = Readonly<Point>;
// { readonly x: number; readonly y: number }
let point: ReadonlyPoint = { x: 1, y: 2 };
// point.x = 10; // ❌ Read-only properties cannot be modified.
// Common Uses:Function Parameter Protection
function freezeConfig(config: Readonly<Config>): void {
// config.host = "other"; // ❌ Modifications are not allowed.
console.log("Configuration Frozen");
}
▶ 例:CRUD における型変換
TYPESCRIPT
interface Article {
id: number;
title: string;
content: string;
author: string;
createdAt: Date;
updatedAt: Date;
}
// At the time of creation:Not necessary id and timestamps
type CreateArticle = Omit<Article, "id" | "createdAt" | "updatedAt">;
// When updating:All fields are optional
type UpdateArticle = Partial<Omit<Article, "id" | "createdAt">>;
// List View:Show only some fields
type ArticleSummary = Pick<Article, "id" | "title" | "author" | "createdAt">;
// Create
let newArticle: CreateArticle = {
title: "TypeScriptGetting Started",
content: "TypeScript is a superset of JavaScript...",
author: "Charlie"
};
// Update
let updateData: UpdateArticle = {
title: "TypeScript Advanced",
content: "An In-Depth Understanding of Generics..."
};
// List
let summary: ArticleSummary = {
id: 1,
title: "TypeScriptGetting Started",
author: "Charlie",
createdAt: new Date()
};
console.log("Create:" + newArticle.title);
console.log("Update:" + (updateData.title ?? "No changes"));
console.log("Abstract:" + summary.title);
出力:
TEXT
Create:TypeScriptGetting Started
Update:TypeScript Advanced
Abstract:TypeScriptGetting Started
2. 属性選択クラス
(1) 選択—特定の属性を選択する
TYPESCRIPT
interface User {
id: number;
name: string;
email: string;
password: string;
role: string;
}
// Pick Select the specified property
type UserPublic = Pick<User, "id" | "name" | "email">;
// { id: number; name: string; email: string }
let publicProfile: UserPublic = {
id: 1,
name: "Charlie",
email: "xiao@example.com"
};
// None password and role —— Safely Disclosing Public Information
(2) 省略—特定の属性を除外する
TYPESCRIPT
// Omit Exclude the specified properties(Pick the opposite of)
type UserSafe = Omit<User, "password">;
// { id: number; name: string; email: string; role: string }
let safeUser: UserSafe = {
id: 1,
name: "Charlie",
email: "xiao@example.com",
role: "admin"
};
// password Excluded
(3) 選ぶか、省くか:選択
TYPESCRIPT
// Retain a few properties → Pick More concise
type Mini = Pick<User, "id" | "name">; // 2a property → Pick
// Exclude a few attributes → Omit More concise
type NoPassword = Omit<User, "password">; // Exclusion1 → Omit
// Preserve Most Properties → Omit More concise
type AlmostAll = Omit<User, "password">; // Retain4 → Omit
// Exclude Most Attributes → Pick More concise
type OnlyTwo = Pick<User, "id" | "name">; // Exclusion3 → Pick
3. ユニオン型操作クラス
(1) 除外—和集合型から除外する
TYPESCRIPT
type AllTypes = "a" | "b" | "c" | "d";
// Exclude Exclude Specified Members
type WithoutA = Exclude<AllTypes, "a">; // "b" | "c" | "d"
type WithoutAB = Exclude<AllTypes, "a" | "b">; // "c" | "d"
(2) 抽出—和集合型からの抽出
TYPESCRIPT
type Mixed = string | number | boolean | null;
// Extract Extract Specified Members
type OnlyString = Extract<Mixed, string>; // string
type StringOrNumber = Extract<Mixed, string | number>; // string | number
(3) NonNullable — null および undefined を除外する
TYPESCRIPT
type MaybeString = string | null | undefined;
// NonNullable Exclusion null and undefined
type DefiniteString = NonNullable<MaybeString>; // string
▶ 例:無効な値のフィルタリング
TYPESCRIPT
type EventName = "click" | "focus" | "blur" | null | undefined;
// Exclusion null and undefined
type ValidEvent = NonNullable<EventName>; // "click" | "focus" | "blur"
// Keep only mouse events
type MouseEvent = Extract<ValidEvent, "click">; // "click"
// Exclusion click Unforeseen events
type NonClick = Exclude<ValidEvent, "click">; // "focus" | "blur"
function handleEvent(event: ValidEvent): void {
console.log(`Handling Events:${event}`);
}
handleEvent("click"); // ✅
handleEvent("focus"); // ✅
// handleEvent(null); // ❌ NonNullable Ruled out
4. 関数型の操作クラス
(1) ReturnType—関数の戻り値の型を取得する
TYPESCRIPT
function createUser(name: string, age: number) {
return { name, age, active: true };
}
// ReturnType Get the return type——No handwriting required
type User = ReturnType<typeof createUser>;
// { name: string; age: number; active: boolean }
let user: User = { name: "Diana", age: 22, active: false };
(2) パラメータ — 関数のパラメータ型のタプルを取得する
TYPESCRIPT
function register(name: string, email: string, age: number): void {}
// Parameters Get Parameter Type
type RegisterParams = Parameters<typeof register>;
// [string, string, number]
let params: RegisterParams = ["Charlie", "xiao@example.com", 20];
(3) ConstructorParameters—コンストラクタの引数の型を取得する
TYPESCRIPT
class Point {
constructor(public x: number, public y: number, public z?: number) {}
}
type PointParams = ConstructorParameters<typeof Point>;
// [number, number, number?]
let args: PointParams = [1, 2];
let point = new Point(...args);
(4) InstanceType—コンストラクタのインスタンス型を取得する
TYPESCRIPT
class Session {
constructor(public token: string) {}
isValid(): boolean { return this.token.length > 0; }
}
type SessionInstance = InstanceType<typeof Session>;
// Equivalent to Session Type
let session: SessionInstance = new Session("abc123");
5. レコード—キー・値ペア型の構築
Record は、最も一般的に使用されるデータ型のひとつであり、「キーと値」の対応関係を素早く作成するための型です:
TYPESCRIPT
// Basic Usage:Key and Value Types
type StringMap = Record<string, string>;
let translations: StringMap = {
hello: "Hello",
goodbye: "Goodbye"
};
// Coordinated Literal-Union Types——Precision Control Key
type Theme = "light" | "dark";
type ThemeColors = Record<Theme, { bg: string; text: string }>;
let themes: ThemeColors = {
light: { bg: "#ffffff", text: "#333333" },
dark: { bg: "#1a1a1a", text: "#e0e0e0" }
};
// Abbreviation: Use Record instead of handwritten Object Types
type Scores = Record<"Chinese Language" | "Mathematics" | "English", number>;
let myScores: Scores = { Chinese Language: 90, Mathematics: 95, English: 88 };
6. ツールタイプの仕組み
ツールタイプの内部実装を理解しておくと、それらをカスタマイズする際に役立ちます:
(1) Partialの実装
TYPESCRIPT
type Partial<T> = {
[K in keyof T]?: T[K];
};
keyof T—— T のすべてのプロパティキーの集合型[K in keyof T]— 各プロパティキー(マップ型)を順に処理する?— オプションの修飾子を追加するT[K]— 元の属性値の型を保持する
(2) Readonlyの実装
TYPESCRIPT
type Readonly<T> = {
readonly [K in keyof T]: T[K];
};
(3) Pickの実装
TYPESCRIPT
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
(4) Omitの実装
TYPESCRIPT
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
(5) Recordの実装
TYPESCRIPT
type Record<K extends keyof any, T> = {
[P in K]: T;
};
▶ 例:カスタムツールタイプ
TYPESCRIPT
// DeepPartial——Recursion makes all levels optional
type DeepPartial<T> = {
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
};
interface Config {
server: {
host: string;
port: number;
};
database: {
url: string;
pool: {
min: number;
max: number;
};
};
}
type PartialConfig = DeepPartial<Config>;
// Properties at all levels are now optional
let config: PartialConfig = {
server: { host: "localhost" } // port Can be omitted
// database The entire section can be omitted.
};
// DeepReadonly——Recursively set all levels to read-only
type DeepReadonly<T> = {
readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K];
};
type FrozenConfig = DeepReadonly<Config>;
// config.server.host = "other"; // ❌ Deep Read-Only
❓ よくある質問
Q
Partialとオプションのプロパティの違いは何ですか?A オプションのプロパティは、
? when defining an interface. Partial is a tool type—it automatically makes all properties of an existing type optional. The difference is that Partial is "derived from an existing type" and does not require redefining the interface. In actual development, the UpdateUser = Partial<User>という形式で手動でマークされます。更新操作が最も一般的な使用例です。Q
Pick と Omit、どちらを使うべきですか?A どちらがより簡潔かによって異なります。保持するプロパティが少ない場合は
Pick を、除外するプロパティが少ない場合は Omit を使用してください。この2つは互いに補完し合う関係にあるため、より短い方を選んでください。コードの可読性が重要です。Omit<User, "password">はPick<User, "id" | "name" | "email" | "role">よりもはるかに分かりやすいです。Q
ReturnType を使って、非同期関数の戻り値の型を特定できますか?A 非同期関数は Promise を返します。
ReturnType<typeof asyncFn> は T ではなく Promise<T> を返します。Awaited<ReturnType<typeof asyncFn>> を使用して Promise をアンラップする必要があります(TypeScript 4.5 以降には Awaited 型が含まれています)。Q ツール型はパフォーマンスに影響しますか?
A いいえ。ツール型はあくまでコンパイル時の概念であり、コンパイル後にすべての型情報が消去されるため、実行時のオーバーヘッドはゼロです。ただし、過度に複雑な型のネストがあるとコンパイル時間が長くなる可能性がありますが、実際の影響はごくわずかです。
📖 まとめ
- 部分的/必須/読み取り専用:変換属性のオプション性および読み取り専用性
- 選択/除外:指定された属性を選択または除外する――より簡潔な選択肢を選ぶ
- レコード:キーと値のペア型を素早く作成し、リテラル集合型と組み合わせてキーを正確に制御できます
- 複合型のメンバーに対する除外・抽出・NULL不可の操作
- ReturnType/Parameters/ConstructorParameters: 関数に関する型情報を抽出します
- ツールタイプはマップタイプに基づいています +
keyof—その仕組みを理解すれば、独自のツールタイプを作成できます
📝 練習問題
- 基本演習(難易度 ⭐):
Todoインターフェース(id、title、completed、createdAt)を定義し、ツールタイプを使用して以下を作成してください:CreateTodo(作成時には id と time は必須ではありません)、UpdateTodo(更新時にはすべてのフィールドがオプションです)、およびTodoPreview(title と completed のみを表示します)。 - 上級演習(難易度 ⭐⭐):
Mutable<T>ツールタイプをカスタマイズし、readonly修飾子をすべて削除します(-readonly修飾子マッピングを使用)。次に、Readonly<Config>を使用して読み取り専用の構成を作成し、Mutable<Readonly<Config>>を使用して書き込み可能状態が復元されたことを確認してください。 - 課題(難易度:⭐⭐⭐):
PathKeys<T>ツールタイプを実装し、ネストされたオブジェクトのすべてのプロパティパスを再帰的に抽出してください。例:{ user: { name: string; address: { city: string } } }→"user" | "user.name" | "user.address" | "user.address.city"。



