型安全性を保ちながらコードを柔軟にする! TypeScriptにおけるジェネリック型のオプション化
TypeScriptでジェネリック型をオプションにする方法
デフォルト値を使う
ジェネリック型にデフォルト値を設定することで、ジェネリック型を省略することができます。例えば、以下のコードでは、T
型にデフォルト値としてstring
型を設定しています。
function foo<T = string>(arg: T): T {
return arg;
}
const bar = foo(123); // bar: number
const baz = foo("abc"); // baz: string
このコードでは、foo
関数を呼び出す際に、ジェネリック型を省略することができます。bar
変数には数値123
、baz
変数には文字列"abc"
が格納されます。
undefinedまたはnullを使う
ジェネリック型にundefined
またはnull
を設定することで、ジェネリック型を省略することができます。ただし、この方法を使う場合は、ジェネリック型がundefined
またはnull
になる可能性があることを考慮する必要があります。
function foo<T extends undefined | null>(arg: T): T {
return arg;
}
const bar = foo(undefined); // bar: undefined
const baz = foo(null); // baz: null
Partial
型を使うことで、ジェネリック型のプロパティをオプションにすることができます。例えば、以下のコードでは、T
型のプロパティname
とage
をオプションにしています。
interface Person {
name: string;
age: number;
}
function foo<T extends Partial<Person>>(arg: T): T {
return arg;
}
const bar = foo({ name: "John" }); // bar: { name: "John" }
const baz = foo({ age: 30 }); // baz: { age: 30 }
このコードでは、foo
関数を呼び出す際に、ジェネリック型T
のプロパティname
とage
を省略することができます。bar
変数にはname
プロパティのみを持つオブジェクト、baz
変数にはage
プロパティのみを持つオブジェクトが格納されます。
Union型を使うことで、ジェネリック型に複数の型を指定することができます。例えば、以下のコードでは、T
型にstring
型とnumber
型を指定しています。
function foo<T extends string | number>(arg: T): T {
return arg;
}
const bar = foo("abc"); // bar: string
const baz = foo(123); // baz: number
TypeScriptでジェネリック型をオプションにする方法はいくつかあります。それぞれの方法にはメリットとデメリットがあるので、状況に合わせて使い分けることが重要です。
デフォルト値を使う
function foo<T = string>(arg: T): T {
return arg;
}
const bar = foo(123); // bar: number
const baz = foo("abc"); // baz: string
undefinedまたはnullを使う
function foo<T extends undefined | null>(arg: T): T {
return arg;
}
const bar = foo(undefined); // bar: undefined
const baz = foo(null); // baz: null
Partial型を使う
interface Person {
name: string;
age: number;
}
function foo<T extends Partial<Person>>(arg: T): T {
return arg;
}
const bar = foo({ name: "John" }); // bar: { name: "John" }
const baz = foo({ age: 30 }); // baz: { age: 30 }
Union型を使う
function foo<T extends string | number>(arg: T): T {
return arg;
}
const bar = foo("abc"); // bar: string
const baz = foo(123); // baz: number
ジェネリック型を複数回使う
function foo<T, U>(arg1: T, arg2: U): [T, U] {
return [arg1, arg2];
}
const bar = foo("abc", 123); // bar: ["abc", 123]
ジェネリック型に制約をつける
function foo<T extends number | string>(arg: T): T {
return arg;
}
// エラー: 'boolean'型は'number'型または'string'型ではない
// const bar = foo(true);
ジェネリック型を利用した関数型
type Foo<T> = (arg: T) => T;
const bar: Foo<string> = (arg) => arg;
const baz = bar("abc"); // baz: "abc"
ジェネリック型をオプションにする他の方法
interface Person {
name: string;
age: number;
}
function foo<T extends Partial<Person>>(arg: T): T {
return arg;
}
const bar = foo({ name: "John" }); // bar: { name: "John" }
const baz = foo({ age: 30 }); // baz: { age: 30 }
const qux = foo({ name: "John", age: 30 }); // qux: { name: "John", age: 30 }
interface Person {
name: string;
age: number;
}
function foo<T extends keyof Person>(arg: T): Person[T] {
return arg;
}
const bar = foo("name"); // bar: string
const baz = foo("age"); // baz: number
Record
型を使うことで、ジェネリック型をキーと値のペアの型にすることができます。例えば、以下のコードでは、T
型をキーと値のペアの型にしています。
function foo<T extends Record<string, any>>(arg: T): T {
return arg;
}
const bar = foo({ name: "John" }); // bar: { name: "John" }
const baz = foo({ age: 30 }); // baz: { age: 30 }
TypeScriptのジェネリック型は、コードを汎用化するための強力なツールです。上記のサンプルコードを参考に、さまざまな状況でジェネリック型を活用してみてください。
generics typescript