こんにちは、フロントエンドエンジニアのまさにょんです。
今回は、TypeScriptでのGenericsの型引数に制約をかける方法について解説していきます。
そもそもGenericsって何?と言う方は、ぜひ次の記事をCheckしてみてください!
以前、Genericsについて解説して、そこでも型引数に制約をかける方法についても説明しましたが、実装の中で新しい方法を発見したのでご紹介します。
目次
「extends」でGenericsの型引数に制約をかける
Genericsとは、型を汎用化・抽象化するための記法で、interfaceや関数、Classなどを「汎用パーツ」(Generics-Parts)にすることができます。
Genericsの基本的な使い方や概念については、上記の『ジェネリクス(Generics)とは?』でCheckしてみてください!
そんな「汎用パーツ」(Generics-Parts)を作ることができるGenericsですが、型引数に制約をかける方法は「2通り」あります。
- 「 extends 」で「型引数」に「継承-Type」を指定する方法
- 「 = 」で「型引数」に入るTypeを指定する方法
それでは、それぞれの使い方をCodeで確認していきましょう。
// Generics の型引数に制約をかける!
// Generics => 汎用化・抽象化 => 汎用パーツ(Generics-Parts)にしている!
// Generics の型引数に制約をかける方法は、2通りある!
// 「 = 」または「 extends 」
// < extends-Pattern >
// 1. extends-Pattern => extends で「型引数」に「継承-Type」を指定する => 型引数に制約をかける!
interface RobotamaType<F, L extends string | number, R extends boolean | number > {
firstName: F; // string or number
lastName: L; // string or number
robotamaFlag: R; // boolean or number
}
// 2. Generics を具体化・実体化する! => Generics-Instance(汎用パーツの実体化)
const robotamaInstance: RobotamaType<string, number, boolean> = {
firstName: 'ロボ玉試作1号機',
lastName: 1,
robotamaFlag: true
}
「 = 」でGenericsの型引数に制約をかける
続いて「 = 」で「型引数」に入るTypeを指定する方法を見ていきましょう。
// Generics の型引数に制約をかける!
// Generics => 汎用化・抽象化 => 汎用パーツ(Generics-Parts)にしている!
// Generics の型引数に制約をかける方法は、2通りある!
// 「 = 」または「 extends 」
// < 「 = 」Pattren >
// 1. 「 = 」Pattern => 「 = 」で「型引数」に入るTypeを指定する => 型引数に制約をかける!
interface RobotamaType2<F, L = string | number, R = boolean | number > {
firstName: F; // string or number
lastName: L; // string or number
robotamaFlag: R; // boolean or number
}
// 2. Generics を具体化・実体化する! => Generics-Instance(汎用パーツの実体化)
const robotamaInstance2: RobotamaType<string, number, number> = {
firstName: 'ロボ玉試作2号機',
lastName: 2,
robotamaFlag: 1
}
おまけ: より実践的な、Genericsの活用技
Literal-Union-Types で型に制約をかけることをよくすると思いますが、Genericsでももちろん使用できます。
// < おまけ: より実践的な、Genericsの活用技 >
// 1. Literal-Union-Types を作成する!
const specialSkills = ['ロボ玉ビーム', 'ロボ玉バズーカ', 'ロボ玉サーベル'] as const;
type SpecialSkills = typeof specialSkills[number];
// 2. Literal-Union-Types のみを受け付ける「型変数」を設定 & Generics-Type である「Robotama-Type」も継承している!
interface SuperRobotamaParams<S = SpecialSkills> extends RobotamaType<string, string, boolean> {
power: number;
defense: number;
special: S;
}
// 3. special に設定できるのは、制約をかけている「SpecialSkills」(Literal-Union-Types)のみ!
const superRobotama: SuperRobotamaParams = {
firstName: 'グンマー帝国のロボ・',
lastName: 'ロボ玉',
robotamaFlag: true,
power: 1000,
defense: 1000,
special: 'ロボ玉ビーム' // 「SpecialSkills」のどれかに制約
}
// --------------------------------------------------------------------------------------
// Robotama-Typeの定義
interface RobotamaType<F, L extends string | number, R extends boolean | number > {
firstName: F; // string or number
lastName: L; // string or number
robotamaFlag: R; // boolean or number
}
const robotamaInstance: RobotamaType<string, number, boolean> = {
firstName: 'ロボ玉試作1号機',
lastName: 1,
robotamaFlag: true
}
まとめ: Sample-Code-全文
最後に、今回紹介したCodeの全文を掲載しておきます。
ぜひ活用してみてください!
// Generics の型引数に制約をかける!
// Generics => 汎用化・抽象化 => 汎用パーツ(Generics-Parts)にしている!
// Generics の型引数に制約をかける方法は、2通りある!
// 「 = 」または「 extends 」
// < extends-Pattern >
// 1. extends-Pattern => extends で「型引数」に「継承-Type」を指定する => 型引数に制約をかける!
interface RobotamaType<F, L extends string | number, R extends boolean | number > {
firstName: F; // string or number
lastName: L; // string or number
robotamaFlag: R; // boolean or number
}
// 2. Generics を具体化・実体化する! => Generics-Instance(汎用パーツの実体化)
const robotamaInstance: RobotamaType<string, number, boolean> = {
firstName: 'ロボ玉試作1号機',
lastName: 1,
robotamaFlag: true
}
// --------------------------------------------------------------------------------------
// < 「 = 」Pattren >
// 1. 「 = 」Pattern => 「 = 」で「型引数」に入るTypeを指定する => 型引数に制約をかける!
interface RobotamaType2<F, L = string | number, R = boolean | number > {
firstName: F; // string or number
lastName: L; // string or number
robotamaFlag: R; // boolean or number
}
// 2. Generics を具体化・実体化する! => Generics-Instance(汎用パーツの実体化)
const robotamaInstance2: RobotamaType<string, number, number> = {
firstName: 'ロボ玉試作2号機',
lastName: 2,
robotamaFlag: 1
}
// --------------------------------------------------------------------------------------
// < おまけ: より実践的な、Genericsの活用技 >
// 1. Literal-Union-Types を作成する!
const specialSkills = ['ロボ玉ビーム', 'ロボ玉バズーカ', 'ロボ玉サーベル'] as const;
type SpecialSkills = typeof specialSkills[number];
// 2. Literal-Union-Types のみを受け付ける「型変数」を設定 & Generics-Type である「Robotama-Type」も継承している!
interface SuperRobotamaParams<S = SpecialSkills> extends RobotamaType<string, string, boolean> {
power: number;
defense: number;
special: S;
}
// 3. special に設定できるのは、制約をかけている「SpecialSkills」(Literal-Union-Types)のみ!
const superRobotama: SuperRobotamaParams = {
firstName: 'グンマー帝国のロボ・',
lastName: 'ロボ玉',
robotamaFlag: true,
power: 1000,
defense: 1000,
special: 'ロボ玉ビーム' // 「SpecialSkills」のどれかに制約
}