TypeScriptの条件付き型… それらはカメレオンのように、コードのニーズに応じて形を変え、適応します!基本的な型にはすでに慣れているかもしれませんが、条件付き型はそれを次のレベルへと引き上げます。型付けのスーパーヒーローのように、状況に応じて適応するスマートな型だと考えてください。では、TypeScriptコードに魔法を加える準備をしましょう!🧙♂
なぜ条件付き型を使うのか?🧐
TypeScriptでは、すべてが整然と整理され、厳密に型付けされていることが理想的です。しかし時には、特定の条件に基づいて適応する型が必要になります。入力パラメータに応じてstring
かnumber
のどちらかを返す関数があるとしましょう。😱 条件付き型がなければ、各ケースを手動で管理する必要があり、それはすぐに面倒になってしまいます!幸運なことに、条件付き型は型付け忍者のように柔軟性と応答性をもたらします。🥷
基本:条件付き型の構文 🧩
条件付き型は型のための三項演算子のようなものです。例を見てみましょう:
1type IsString<T> = T extends string ? "It's a string" : "It's not a string";
この例では、IsString
はT
がstring
かどうかをチェックする条件付き型です。もしそうであれば"It's a string"
を、そうでなければ"It's not a string"
を返します。単純そうですが、より高度なケースで何ができるか見てみましょう!
実践例:設定に基づく型の適応 🛠️
設定パラメータを受け取り、その設定に応じて異なる型を返す必要がある関数を想像してみましょう。条件付き型はこのような魔法に最適です ✨!
コード例
1type Config = {
2 mode: 'simple' | 'detailed';
3};
4
5type Response<T extends Config> = T['mode'] extends 'simple'
6 ? { data: string }
7 : { data: string; details: string[] };
8
9function fetchData<T extends Config>(config: T): Response<T> {
10 if (config.mode === 'simple') {
11 return { data: 'Simplified data' } as Response<T>;
12 } else {
13 return {
14 data: 'Full data',
15 details: ['detail1', 'detail2'],
16 } as Response<T>;
17 }
18}
19
20const simpleConfig = { mode: 'simple' };
21const detailedConfig = { mode: 'detailed' };
22
23const resultSimple = fetchData(simpleConfig); // { data: "Simplified data" }
24const resultDetailed = fetchData(detailedConfig); // { data: "Full data", details: [...] }
この例では、Response<T>
はmode
が"simple"
か"detailed"
かに基づいて型を適応させます。fetchData(simpleConfig)
はdata
のみを含むオブジェクトを返し、fetchData(detailedConfig)
はdetails
も含むという魔法が起こります。便利ですよね?
ネストされた条件付き型:アラカルト型付け 🧇
そこで止める必要はありません!条件付き型をネストして、さらに正確なケースを処理することもできます!mode
だけでなく、ユーザーが認証されているかどうかに基づいて型を適応させたいとしましょう。高度な型のジャグリングの準備はできていますか?🎢
1type UserResponse<
2 T extends Config,
3 Authenticated extends boolean,
4> = Authenticated extends true
5 ? T['mode'] extends 'simple'
6 ? { data: string }
7 : { data: string; details: string[] }
8 : { error: 'Not authenticated' };
9
10function fetchUserData<T extends Config, Authenticated extends boolean>(
11 config: T,
12 isAuthenticated: Authenticated,
13): UserResponse<T, Authenticated> {
14 if (!isAuthenticated) {
15 return { error: 'Not authenticated' } as UserResponse<T, Authenticated>;
16 }
17 if (config.mode === 'simple') {
18 return { data: 'Simplified data' } as UserResponse<T, Authenticated>;
19 } else {
20 return {
21 data: 'Full data',
22 details: ['detail1', 'detail2'],
23 } as UserResponse<T, Authenticated>;
24 }
25}
26
27const resultAuthSimple = fetchUserData(simpleConfig, true); // { data: "Simplified data" }
28const resultAuthDetailed = fetchUserData(detailedConfig, true); // { data: "Full data", details: [...] }
29const resultNotAuth = fetchUserData(detailedConfig, false); // { error: "Not authenticated" }
ここでは、UserResponse
はmode
とisAuthenticated
という2つの基準に基づいて型を適応させます。結果として、すべての可能なケースをカバーする超精密な型付けが実現します!
infer
を使用した高度な条件付き型:型推論 🕵️♂️
高度なテクニックの準備はできていますか?TypeScriptには条件付き型で使用する特別なキーワードinfer
があります。これにより、条件付き型の中で直接型を推論できます。複雑な型から情報を抽出するのに便利です!
infer
の例
1type ReturnTypeOfFunction<T> = T extends (...args: any[]) => infer R
2 ? R
3 : never;
4
5function getHello(): string {
6 return 'Hello, world!';
7}
8
9type HelloReturnType = ReturnTypeOfFunction<typeof getHello>; // 結果: string
ここでは、ReturnTypeOfFunction
はinfer R
を使用して関数の戻り値の型を推論します。この例では、getHello
は文字列を返すため、HelloReturnType
はstring
型になります。
結論 🎉
TypeScriptの条件付き型は、コードのスーパーパワーのようなものです。動的な型を作成し、厳密な型付けを維持しながら、コードをよりシンプルにすることができます。同僚を感動させる準備はできていますか?これらの条件付き型を試して、TypeScriptコードを構造化する新しい方法を発見しましょう!🚀
もう曖昧な型付けには言い訳はありません—条件付き型を使えば、TypeScriptコードは事実上バレットプルーフになります!👌