MochiuWiki : SUSE, EC, PCB
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
検索
個人用ツール
ログイン
Toggle dark mode
名前空間
ページ
議論
表示
閲覧
ソースを閲覧
履歴を表示
TypeScriptの基礎 - ユーティリティ型(基本)のソースを表示
提供: MochiuWiki : SUSE, EC, PCB
←
TypeScriptの基礎 - ユーティリティ型(基本)
あなたには「このページの編集」を行う権限がありません。理由は以下の通りです:
この操作は、次のグループのいずれかに属する利用者のみが実行できます:
管理者
、new-group。
このページのソースの閲覧やコピーができます。
== 概要 == ユーティリティ型 (Utility Types) とは、TypeScriptに組み込まれている型変換のための汎用型である。<br> マップ型 (Mapped Types) を活用して既存の型から新しい型を効率的に生成できる。<br> <br> ユーティリティ型を使用することで、型定義の重複を避けてコードの保守性を高めることができる。<br> 例えば、全プロパティをオプショナルにしたい場合や、特定のプロパティだけを取り出したい場合に、元の型を修正せずに新しい型を導出できる。<br> <br> ユーティリティ型はTypeScriptの標準ライブラリに定義されており、追加のインポートなしに使用できる。<br> 内部定義はマップ型やConditional型 (条件型) を組み合わせた構造になっており、TypeScriptの型システムの表現力の高さを示す好例でもある。<br> <br> <center> {| class="wikitable" |+ 基本ユーティリティ型の概要 ! ユーティリティ型 !! 説明 |- | <code>Partial<T></code> || 型Tの全プロパティをオプショナル (任意) にする。 |- | <code>Required<T></code> || 型Tの全プロパティを必須にする。 |- | <code>Readonly<T></code> || 型Tの全プロパティを読み取り専用にする。 |- | <code>Pick<T, K></code> || 型Tから指定したプロパティKのみを選択する。 |- | <code>Omit<T, K></code> || 型Tから指定したプロパティKを除外する。 |- | <code>Record<K, V></code> || キーの型Kと値の型Vを指定したオブジェクト型を生成する。 |} </center> <br> ジェネリクスの基本については[[TypeScriptの基礎 - ジェネリクスの基本]]のページ、ジェネリクスの制約については[[TypeScriptの基礎 - ジェネリクスの制約]]のページを参照すること。<br> <br><br> == Partial<T> == <code>Partial<T></code> は、型Tの全プロパティをオプショナル (任意) に変換するユーティリティ型である。<br> オプショナルなプロパティは、値を指定しなかった場合に <u>undefined</u> となる。<br> <br> ==== 内部定義 ==== <code>Partial<T></code> の内部定義は以下の通りである。<br> <br> マップ型の構文 <code>[K in keyof T]</code> で型Tの全プロパティを列挙し、<code>?</code> 修飾子を付加することでオプショナルに変換している。<br> <br> <syntaxhighlight lang="typescript"> type Partial<T> = { [K in keyof T]?: T[K]; }; </syntaxhighlight> <br> <code>strictNullChecks</code> が有効な場合、オプショナルなプロパティの型は自動的に <u>undefined</u> を含む型 (<code>T[K] | undefined</code>) となる。<br> <br> ==== 実用例 : APIの更新関数 ==== APIの更新エンドポイントでは、全フィールドではなく変更したいプロパティのみを送信することが多い。<br> <code>Partial<T></code> を使用することにより、更新時の引数型を簡潔に定義できる。<br> <br> 以下の例では、<u>Partial<User></u> により <u>changes</u> の全プロパティがオプショナルになるため、更新したいプロパティのみを渡せる。<br> <br> <syntaxhighlight lang="typescript"> interface User { id: string; name: string; email: string; age: number; } function updateUser(id: string, changes: Partial<User>): void { // nameだけを更新、emailとageは指定しなくてもよい } updateUser("123", { name: "Alice" }); // OK </syntaxhighlight> <br> ==== 実用例 : 設定のマージ ==== デフォルト設定とユーザ指定の上書き設定をマージする関数にも <code>Partial<T></code> が有効である。<br> <br> <syntaxhighlight lang="typescript"> interface Config { host: string; port: number; timeout: number; debug: boolean; } function mergeConfig(defaults: Config, overrides: Partial<Config>): Config { return { ...defaults, ...overrides }; } </syntaxhighlight> <br> <code>overrides</code> の型を <u>Partial<Config></u> にすることにより、上書きしたいプロパティのみを指定できる。<br> スプレッド演算子 <code>{ ...defaults, ...overrides }</code> により、指定されたプロパティはデフォルト値を上書きし、指定されなかったプロパティはデフォルト値が維持される。<br> <br><br> == Required<T> == <code>Required<T></code> は、型 T の全プロパティを必須にするユーティリティ型である。<br> <code>Partial<T></code> の逆の操作であり、オプショナルなプロパティの <code>?</code> 修飾子を全て除去する。<br> <br> ==== 内部定義 ==== <code>Required<T></code> の内部定義は以下の通りである。<br> <br> <code>-?</code> という修飾子により、マップ型で列挙した全プロパティからオプショナル修飾子 <code>?</code> を除去している。<br> <br> <syntaxhighlight lang="typescript"> type Required<T> = { [K in keyof T]-?: T[K]; }; </syntaxhighlight> <br> <code>-?</code> はマップ型専用の修飾子除去構文であり、通常のTypeScript記法には存在しない特殊な構文である。<br> <br> ==== 実用例 : バリデーション後の型 ==== フォームの入力値は全フィールドがオプショナルで定義されることが多いが、バリデーション後は全フィールドが存在することが保証される。<br> このような場面で <code>Required<T></code> を使用することにより、バリデーション後の型を安全に表現できる。<br> <br> <syntaxhighlight lang="typescript"> interface UserInput { name?: string; email?: string; age?: number; } function validateUser(input: UserInput): Required<UserInput> { if (!input.name || !input.email || !input.age) { throw new Error("All fields are required"); } return input as Required<UserInput>; } </syntaxhighlight> <br> バリデーション後の戻り値型を <code>Required<UserInput></code> とすることにより、呼び出し元では全プロパティが存在することが型システムによって保証される。<br> <br><br> == Readonly<T> == <code>Readonly<T></code> は、型 T の全プロパティを読み取り専用 (<code>readonly</code>) にするユーティリティ型である。<br> 読み取り専用プロパティへの再代入はコンパイルエラーとなる。<br> <br> ==== 内部定義 ==== <code>Readonly<T></code> の内部定義は以下の通りである。<br> <br> マップ型の各プロパティに <code>readonly</code> 修飾子を付加することで読み取り専用に変換している。<br> <br> <syntaxhighlight lang="typescript"> type Readonly<T> = { readonly [K in keyof T]: T[K]; }; </syntaxhighlight> <br> ==== Object.freeze()との関連 ==== <code>Readonly<T></code> は型レベルの制約であり、実行時には効果を持たない。<br> コンパイル後のJavaScriptには <code>readonly</code> 修飾子の情報は残らないため、実行時にプロパティを変更することは技術的には可能である。<br> <br> 実行時のイミュータビリティを確保するためには、<code>Object.freeze()</code> と組み合わせて使用する。<br> <br> * <code>Readonly<T></code> *: コンパイル時にプロパティへの再代入をエラーとして検出する。 * <code>Object.freeze()</code> *: 実行時にオブジェクトのプロパティ変更を防ぐ。 <br> ==== 実用例 : イミュータブルな状態管理 ==== 設定オブジェクトや定数として扱うオブジェクトに <code>Readonly<T></code> を適用することで、誤った変更を防げる。<br> <br> <syntaxhighlight lang="typescript"> interface Config { host: string; port: number; timeout: number; debug: boolean; } const config: Readonly<Config> = { host: "localhost", port: 8080, timeout: 5000, debug: false }; config.host = "127.0.0.1"; // コンパイルエラー : 読み取り専用プロパティに代入できない </syntaxhighlight> <br> <code>Readonly<T></code> はトップレベルのプロパティのみを読み取り専用にする。<br> ネストされたオブジェクトのプロパティは読み取り専用にならないため、深いイミュータビリティが必要な場合は <code>Readonly<T></code> を再帰的に適用するか、ライブラリを使用することを検討する。<br> <br><br> == Pick<T, K> == <code>Pick<T, K></code> は、型Tから指定したプロパティKのみを選択して新しい型を生成するユーティリティ型である。<br> K には型 T のプロパティ名をユニオン型で指定する。<br> <br> ==== 内部定義 ==== <code>Pick<T, K></code> の内部定義は以下の通りである。<br> <br> 型パラメータKに <code>extends keyof T</code> の制約を設けることにより、存在しないプロパティ名の指定をコンパイル時に検出できる。<br> <br> <syntaxhighlight lang="typescript"> type Pick<T, K extends keyof T> = { [P in K]: T[P]; }; </syntaxhighlight> <br> ==== 実用例 : APIレスポンスの一部取得 ==== APIレスポンスの型から、クライアントに公開してよいプロパティのみを選択した型を定義できる。<br> <br> <syntaxhighlight lang="typescript"> interface UserProfile { id: string; name: string; email: string; password: string; createdAt: Date; updatedAt: Date; } type PublicUserInfo = Pick<UserProfile, "id" | "name" | "email">; // { id: string; name: string; email: string; } </syntaxhighlight> <br> <code>password</code> 等の機密情報を除いた型を <code>Pick<T, K></code> で定義することで、APIレスポンスの型安全性を高められる。<br> <br> ==== 実用例 : フォームの型定義 ==== フォームの種類ごとに必要なフィールドが異なる場合、ベースとなる型から必要なプロパティを選択することで型の重複を避けられる。<br> <br> <syntaxhighlight lang="typescript"> interface UserForm { username: string; password: string; email: string; displayName: string; } type LoginForm = Pick<UserForm, "username" | "password">; type RegisterForm = Pick<UserForm, "username" | "password" | "email">; </syntaxhighlight> <br> フォームの型を個別に定義する代わりに <code>Pick<T, K></code> を使用することで、元の型への変更が全てのフォーム型に自動的に反映される。<br> <br><br> == Omit<T, K> == <code>Omit<T, K></code> は、型Tから指定したプロパティKを除外して新しい型を生成するユーティリティ型である。<br> <br> <code>Pick<T, K></code> の逆の操作であり、除外するプロパティを指定する。<br> <br> ==== 内部定義 ==== <code>Omit<T, K></code> の内部定義は以下の通りである。<br> <br> <code>Omit<T, K></code> は <code>Pick</code> と <code>Exclude</code> を組み合わせて実装されている。<br> <code>Exclude<keyof T, K></code> でTの全プロパティキーからKを除いたキーを取得し、そのキーのみで <code>Pick</code> することで除外を実現している。<br> <br> <syntaxhighlight lang="typescript"> type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>; </syntaxhighlight> <br> ==== 実用例: IDを除いた入力型 ==== データの新規作成時は、サーバ側で自動生成される <code>id</code> や <code>createdAt</code> 等を除いた型を入力として受け取ることが多い。<br> <br> <syntaxhighlight lang="typescript"> interface User { id: string; name: string; email: string; createdAt: Date; } type CreateUserInput = Omit<User, "id" | "createdAt">; // { name: string; email: string; } </syntaxhighlight> <br> <code>Omit<T, K></code> を使用することにより、元の型に新しいプロパティが追加された場合も自動的に入力型に反映される。<br> <br> ==== 実用例 : コンポーネントの型拡張 ==== 既存の型を拡張する時に、一部のプロパティを置き換えたい場合に <code>Omit<T, K></code> が有用である。<br> <br> 以下の例では、<u>BaseButtonProps</u> から <u>disabled</u> プロパティを除いた上で <u>size</u> プロパティを追加した <u>PrimaryButtonProps</u> を定義している。<br> <br> <syntaxhighlight lang="typescript"> interface BaseButtonProps { label: string; onClick: () => void; disabled: boolean; } interface PrimaryButtonProps extends Omit<BaseButtonProps, "disabled"> { size: "small" | "medium" | "large"; } </syntaxhighlight> <br><br> == Record<K, V> == <code>Record<K, V></code> は、キーの型 K と値の型 V を指定したオブジェクト型を生成するユーティリティ型である。<br> Kにはユニオン型やリテラル型等、オブジェクトのキーとして使用できる型を指定する。<br> <br> ==== 内部定義 ==== <code>Record<K, V></code> の内部定義は以下の通りである。<br> <br> 型パラメータKに <code>extends keyof any</code> の制約を設けることで、<br> Kが <code>string</code>、<code>number</code>、<code>symbol</code> のいずれか、またはそのユニオン型であることを保証している。<br> <br> <syntaxhighlight lang="typescript"> type Record<K extends keyof any, T> = { [P in K]: T; }; </syntaxhighlight> <br> ==== 実用例: ルックアップテーブル ==== 特定のキー集合に対してそれぞれの値を紐付けるルックアップテーブルを型安全に定義できる。<br> <br> <syntaxhighlight lang="typescript"> type Status = "pending" | "completed" | "failed"; const statusMessages: Record<Status, string> = { pending: "処理中です", completed: "完了しました", failed: "失敗しました" }; </syntaxhighlight> <br> <u>Status</u> 型のユニオン型をキーに指定することで、全ての状態値に対してメッセージが定義されていることをコンパイル時に検証できる。<br> <u>Status</u> 型に新しい値が追加された場合、<u>statusMessages</u> にも対応するエントリを追加しなければコンパイルエラーとなる。<br> <br> ==== 実用例 : 状態マップ ==== IDをキーとしてエンティティを管理する状態マップにも <code>Record<K, V></code> が有用である。<br> <br> <syntaxhighlight lang="typescript"> interface UserState { name: string; email: string; isActive: boolean; } type UserMap = Record<string, UserState>; const users: UserMap = { "user-001": { name: "Alice", email: "alice@example.com", isActive: true }, "user-002": { name: "Bob", email: "bob@example.com", isActive: false } }; </syntaxhighlight> <br> キーの型を <u>string</u> にすることで任意の文字列IDを使用でき、値の型を <u>UserState</u> にすることでオブジェクトの構造が型安全に保証される。<br> <br><br> == ユーティリティ型の組み合わせ == 複数のユーティリティ型を組み合わせることで、より細かい型変換が実現できる。<br> <u>ただし、組み合わせの順序によって結果が異なる場合があるため注意が必要である。</u><br> <br> ==== Partial + Pick ==== 特定のプロパティを選択した上で全てをオプショナルにする場合は、<code>Partial<Pick<T, K>></code> を使用する。<br> <br> <syntaxhighlight lang="typescript"> interface User { id: string; name: string; email: string; age: number; } type UpdateUserInput = Partial<Pick<User, "name" | "email">>; // { name?: string; email?: string; } </syntaxhighlight> <br> <u>Pick<User, "name" | "email"></u> でnameとemailのみの型を生成し、<u>Partial<...></u> でそれらをオプショナルにしている。<br> <br> ==== Omit + Partial ==== 特定のプロパティを除外した上で残りを全てオプショナルにする場合は、<code>Partial<Omit<T, K>></code> を使用する。<br> <br> <syntaxhighlight lang="typescript"> interface Product { id: string; name: string; price: number; description: string; createdAt: Date; } type PartialProductUpdate = Partial<Omit<Product, "id" | "createdAt">>; // { name?: string; price?: number; description?: string; } </syntaxhighlight> <br> ==== Required + Pick ==== 特定のプロパティを選択した上で全てを必須にする場合は、<code>Required<Pick<T, K>></code> を使用する。<br> <br> <syntaxhighlight lang="typescript"> interface FormData { username?: string; password?: string; email?: string; displayName?: string; } type RequiredFormData = Required<Pick<FormData, "username" | "password">>; // { username: string; password: string; } </syntaxhighlight> <br> ==== APIのCRUD型定義パターン ==== APIのCRUD操作で必要な型を、1つのベース型から複数のユーティリティ型の組み合わせで導出できる。<br> <br> <syntaxhighlight lang="typescript"> interface ApiUser { id: string; name: string; email: string; password: string; createdAt: Date; updatedAt: Date; } // 作成リクエスト : idとタイムスタンプはサーバ側で生成するため除外する type CreateUserRequest = Omit<ApiUser, "id" | "createdAt" | "updatedAt">; // 更新リクエスト : id、パスワード、タイムスタンプを除いた残りをオプショナルにする type UpdateUserRequest = Partial<Omit<ApiUser, "id" | "password" | "createdAt" | "updatedAt">>; // 公開レスポンス : 機密情報であるpasswordを除いた公開可能なプロパティのみを返す type PublicUserResponse = Pick<ApiUser, "id" | "name" | "email">; </syntaxhighlight> <br> 上記のパターンにより、<u>ApiUser</u> を唯一の型定義ソースとして管理でき、プロパティの追加や変更が全ての派生型に自動的に反映される。<br> <br> <u>組み合わせに関して、以下の点に注意すること。</u><br> * <u><code>Partial<Pick<T, K>></code> と <code>Pick<Partial<T>, K></code> は同じ結果になるが、<br>意図を明確にするために外側から内側へ読める順序で記述することを推奨する。</u> * 組み合わせが複雑になりすぎた場合は、中間的な型に名前を付けて可読性を維持する。 <br><br> == 関連情報 == * [[TypeScriptの基礎 - ジェネリクスの基本]] * [[TypeScriptの基礎 - ジェネリクスの制約]] * [[TypeScriptの基礎 - ユーティリティ型(応用)]] <br><br> {{#seo: |title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki |keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,Electric Circuit,Electric,pcb,Mathematics,AVR,TI,STMicro,AVR,ATmega,MSP430,STM,Arduino,Xilinx,FPGA,Verilog,HDL,PinePhone,Pine Phone,Raspberry,Raspberry Pi,C,C++,C#,Qt,Qml,MFC,Shell,Bash,Zsh,Fish,SUSE,SLE,Suse Enterprise,Suse Linux,openSUSE,open SUSE,Leap,Linux,uCLnux,電気回路,電子回路,基板,プリント基板 |description={{PAGENAME}} - 電子回路とSUSE Linuxに関する情報 | This page is {{PAGENAME}} in our wiki about electronic circuits and SUSE Linux |image=/resources/assets/MochiuLogo_Single_Blue.png }} __FORCETOC__ [[カテゴリ:Rust]][[カテゴリ:Web]]
TypeScriptの基礎 - ユーティリティ型(基本)
に戻る。
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
Collapse