【TS中級】#2 stringのユニオン×リテラル型で型安全な開発を【実務で頻出】
まえがき
今回はTypeScriptにおけるstring型のユニオン型とリテラル型について解説します。実務でTypeScriptを使っている方や、これから学ぼうとしている方のために、ユニオン型とリテラル型の基本を理解し、どのように効果的に使用できるかを掘り下げていきます。
今回のテーマ
今回のテーマは、TypeScriptにおけるstring
型のユニオン型とリテラル型の組み合わせについてです。これにより、コードの安全性を高め、型の誤用を防ぐ方法を学びます。
ユニオン型とは
ユニオン型とは、複数の型のいずれかを受け取ることができる型です。
type Status = string | number ;
上記の例では、Status
型の変数は、文字列または数値のいずれかを持つことができます。このように、ユニオン型は複数の型を組み合わせた型定義できます。
リテラル型とは
リテラル型は、具体的な値そのものを型として定義するものです。
type One = 1;
const x: One = 1;
const y: One = 2; // Error: Type '2' is not assignable to type '1'.
この例では、One
という型を割り当てられた変数は、数値の1のみを持つことができるので、最後の行はエラーになります。
ユニオン型とリテラル型の組み合わせ
これらを実際の例で見てみましょう。例えば、ブログのカテゴリを定義する際に、それぞれのカテゴリに対応する英語と日本語の表示名を持つオブジェクトを定義したい場合です(実際の僕の個人開発のコードです)。
export type Category = "Tech" | "DevDiary" | "LifeStyle";
export const category: { [key in Category]: { en: string; ja: string } } = {
Tech: { en: "Tech Articles", ja: "技術記事" },
DevDiary: { en: "Dev Diary", ja: "開発日記" },
LifeStyle: { en: "Lifestyle & Hobby", ja: "雑談" },
};
コードの解説
-
Category
の定義:
Category
型は、"Tech"
,"DevDiary"
,"LifeStyle"
の3つの文字列リテラルのユニオン型です。この型を使用することで、これらの3つの値以外がCategory
として扱われることを防ぎます。 -
category
オブジェクトの定義:
category
オブジェクトは、キーとしてCategory
型を使用し、その値として英語(en
)と日本語(ja
)の表示名を持つオブジェクトを持っています。ここで、key in Category
という記法を用いることで、category
オブジェクトのキーがCategory
型であることを保証しています。タイポや勘違いでCategory
型でないものをオブジェクトに追加しようとするとエラーが出てくれることが重要です。
より踏み込んだ話
上記のコードにおいて、Category
型に新しい文字列"other"
が追加されたら、どこかでエラーが出るでしょうか?
実は、category
オブジェクトに "other"
をキーにもつプロパティがないという旨のエラーが出ます。category
オブジェクトの型定義から、オブジェクトのキーはCategory
型のキーを過不足なく持つことを期待しているので、Category
型を増やした際に、自動でエラーを吐いてくれます。
ただ、エラーが出るのではなく、常にcategory
のキーとCategory
型が一致するようにコーディングできれば一番良いと思う方もいるかもしれません。
これは、category
を手打ちで定義し、そのキーをすべて取得し、それらをユニオン×リテラル型でもつCategory
型を自動で定義するという実装です。
以下がその実装になります。
export const category = {
Tech: { en: "Tech Articles", ja: "技術記事" },
DevDiary: { en: "Dev Diary", ja: "開発日記" },
LifeStyle: { en: "Lifestyle & Hobby", ja: "雑談" },
};
type Category = keyof typeof category;
category
の型をtype of
で取得し、その中のキーをkey of
で取得することで、今までと同様のCategory
型を定義するのと同時に、型の追加や削除がcategory
の変更に依存するようになり変更しやすくなりました。
ちなみに僕の感覚だと定義の向きが逆に感じてしまう(大体のコーダーは 型定義→オブジェクト定義 という感覚を持っている...はず)のでこの実装は行なっていません(大規模開発でヒューマンエラーが出る可能性が高いなどの理由があるならばこの書き方をするべき)。
さらに言うと、category
のvalueの方の型がうまく定義できない(やり方わかる方いればご教授お願いします🙇)ので今回は使いません。
(category: Record<string, { en: string; ja: string }>;
のように定義すると、Category
型がstring
になってしまう)
終わりに
今回は、TypeScriptにおけるstring型のユニオン型とリテラル型について解説しました。これらを活用することで、コードの信頼性を高め、型安全な開発を行うことができます。特に、ユニオン型とリテラル型の組み合わせは、特定の値のセットを扱う際に非常に役立ち、実務でもよく見かけるので覚えておいて損は無いと思います。
お読みいただきありがとうございました🥳
Discussion