TypeScriptの排他的な型について
こんにちは、皆さん!今日はTypeScriptで使える便利な技術、排他的な型についてお話しします。みなさんはTypeScriptを使っていて、「このプロパティは絶対に同時に存在してほしくない!」と思ったことはありませんか?そんなときに役立つのが排他的な型です。
排他的な型とは?
排他的な型とは、特定のプロパティが同時に存在しないことを保証するための型です。これにより、コードの信頼性と安全性を高めることができます。具体的には、あるプロパティが存在するときは別のプロパティが存在しないというルールを定義します。
具体例
例えば、前回書いた記事で以下のような関数があります。この関数は、SMSまたはメールのどちらか一方を送信するためのものです。
const greatToSmsOrEmail = ({
me,
to,
sms,
email,
text,
}: { me: string; to: string; text: string } & ({ sms: PhoneNumber; email?: never } | { email: Email; sms?: never })) => {
// 関数のロジック
};
この関数の引数には排他的な要素があり、sms
とemail
の両方が同時に存在することはできません。どちらか一方のみが存在することを型で保証しています。
排他的な型の定義
このような排他的な型は以下のように定義できます。
type Exclusive<T extends Record<PropertyKey, unknown>, U extends Record<PropertyKey, unknown>> =
| (T & { [k in Exclude<keyof U, keyof T>]?: never })
| (U & { [k in Exclude<keyof T, keyof U>]?: never });
この定義により、T
とU
のプロパティが排他的であることが保証されます。
利用手順
では、実際にどのようにこの型を使うのか見ていきましょう。
ステップ1: 型の定義
まずは先ほど紹介した排他的な型を定義します。
type PhoneNumber = string;
type Email = string;
type Exclusive<T extends Record<PropertyKey, unknown>, U extends Record<PropertyKey, unknown>> =
| (T & { [k in Exclude<keyof U, keyof T>]?: never })
| (U & { [k in Exclude<keyof T, keyof U>]?: never });
ステップ2: 関数の作成
次に、この型を使って関数を作成します。
const sendMessage = (options: { me: string; to: string; text: string } & Exclusive<{ sms: PhoneNumber }, { email: Email }>) => {
if (options.sms) {
console.log(`Sending SMS to ${options.to}`);
} else if (options.email) {
console.log(`Sending Email to ${options.to}`);
}
};
ステップ3: 関数の呼び出し
最後に、この関数を呼び出してみましょう。
sendMessage({ me: 'Alice', to: 'Bob', text: 'Hello', sms: '123-456-7890' });
// もしくは
sendMessage({ me: 'Alice', to: 'Bob', text: 'Hello', email: 'bob@example.com' });
まとめ
排他的な型を使うことで、TypeScriptの型安全性をさらに高めることができます。ぜひ一度試してみてください!何か質問があれば、コメント欄で教えてくださいね。