【TypeScript入門】Generics(ジェネリクス)の型引数に制約をかける

Generics-Params

こんにちは、フロントエンドエンジニアのまさにょんです。

今回は、TypeScriptでのGenericsの型引数に制約をかける方法について解説していきます。

そもそもGenericsって何?と言う方は、ぜひ次の記事をCheckしてみてください!

【ジェネリクス(Generics)とは?】

以前、Genericsについて解説して、そこでも型引数に制約をかける方法についても説明しましたが、実装の中で新しい方法を発見したのでご紹介します。

「extends」でGenericsの型引数に制約をかける

Genericsとは、型を汎用化・抽象化するための記法で、interfaceや関数、Classなどを「汎用パーツ」(Generics-Parts)にすることができます。

Genericsの基本的な使い方や概念については、上記の『ジェネリクス(Generics)とは?』でCheckしてみてください!

そんな「汎用パーツ」(Generics-Parts)を作ることができるGenericsですが、型引数に制約をかける方法は「2通り」あります。

  1. 「 extends 」で「型引数」に「継承-Type」を指定する方法
  2. 「 = 」で「型引数」に入る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」のどれかに制約
}

TypeScript書籍

最近の投稿