💎

【TypeScript】ユーティリティ型のExtractとExcludeをConditional Typesで定義する

2024/10/10に公開

TypeScriptは、強力な型システムを持つことで知られており、開発者がより安全で信頼性の高いコードを書くのを助けます。その中でも、ユーティリティ型は、複雑な型操作を簡潔に表現するための強力なツールです。本記事では、TypeScriptに標準で用意されているユーティリティ型であるExtractExcludeを、Conditional Typesを用いてどのように定義するかを解説します。

ユーティリティ型とは?

ユーティリティ型は、TypeScriptが提供する一般的な型変換を容易にするための組み込みの型変換ツールで、既存の型を元に新しい型を作成するために使用されます。これにより、開発者は複雑な型操作を簡潔に表現でき、コードの再利用性と可読性が向上します。
主なユーティリティ型には以下のようなものがあります:

Partial<T>
Required<T>
Readonly<T>
Pick<T, K>
Omit<T, K>
Extract<T, U>
Exclude<T, U>
ReturnType<T>

他のユーティリティ型が気になる方は、TypeScript公式ドキュメントをご一読ください。
https://www.typescriptlang.org/docs/handbook/utility-types.html

本記事では、特にExtractExcludeに焦点を当て、それらをどのようにConditional Typesで定義できるかを見ていきます。

Conditional Types(条件型)とは?

Conditional Typesは、TypeScriptの高度な型システムの機能の一つで、ある型が特定の条件を満たすかどうかに基づいて型を選択する仕組みです。JavaScriptの条件分岐(if文)に似ていますが、これは型レベルで行われます。

基本的な構文は以下の通りです:

T extends U ? X : Y
  • T extends U: 条件部分。TがUに代入可能かどうかをチェックします。
  • ? X : Y: 条件が真であればXを、偽であればYを返します。

Conditional Typesは、特にジェネリック型と組み合わせて使用されることが多く、柔軟で再利用可能な型定義を可能にします。

Conditional Typesの他の使い方について興味がある方は、TypeScript公式ドキュメントをご一読ください。僕の方でも、いつか別で記事を書きたいと思います。
https://www.typescriptlang.org/docs/handbook/2/conditional-types.html

ExtractとConditional Typesを使った定義

Extract(抽出)とは?

Extractは、ある型Tから、別の型Uに割り当て可能な型のみを抽出するユーティリティ型です。

Extract<Type, Union>

type E1 = Extract<'a' | 'b' | 'c', 'a' | 'f'>; // 'a'
type E2 = Extract<string | number | (() => void), Function>; // () => void

E1の使用例のように、二つのユニオン型の共通点を抽出することにも使えます。

Conditional Typesを使った定義

type MyExtract<T, U> = T extends U ? T : never;
  • T extends U ? T : never: TがUに割り当て可能であればTを返し、そうでなければneverを返します。
  • TypeScriptの条件型は分配的に働き、ユニオン型の各要素に対して条件が適用されます。

ExcludeとConditional Typesを使った定義

Exclude(除外)とは?

Excludeは、ある型Tから、別の型Uに含まれる部分を取り除いた型を生成するユーティリティ型です。
Extractの逆の操作を行います。

Exclude<UnionType, ExcludedMembers>

type E1 = Exclude<'a' | 'b' | 'c', 'a' | 'b'>; // 'c'
type E2 = Exclude<string | number | (() => void), Function>; // string | number

Conditional Typesを使った定義

type MyExclude<T, U> = T extends U ? never : T;
  • T extends U ? never : T: TがUに割り当て可能であればnever(存在しない型)を返し、そうでなければTをそのまま返します。
  • 条件型は分配的に働くため、Tがユニオン型であれば各要素に対して条件が適用されます。

まとめ

TypeScriptのユーティリティ型であるExtractとExcludeは、Conditional Typesを使って同じように定義できることがわかりました。このように、Conditional Typesを学ぶことによってUtility Typesがどのように定義されているか理解できるようになります。
他のUtility Typesについても、別の記事でConditional Typesを使った定義の仕方を解説したいと思いますので、いいねとフォローをよろしくお願いいたします!

Discussion