Typescriptでは予め型を決めておく必要がありますが、ジェネリクスを使うと使用直前まで型を定義しなくて良くなります。
型が違う同じような機能をいくつも定義しなくて良くなるので、ソースコードの再利用性や保守性が増します。
以下では、ジェネリクスを使はない場合はstringとnumberの両方のクラスを用意しないといけないですが、ジェネリクスを使う場合は1つのクラスだけで機能提供できていることがわかります。
ジェネリクスを使わない場合
class useString {
constructor(public arrays: string[]) {}
print(value: string): void {
for(const array of this.arrays) {
if(array === value) {
console.log(`Yes you have ${value}`)
}
}
}
}
class useNumber {
constructor(public arrays: number[]) {}
print(value: number): void {
for(const array of this.arrays) {
if(array === value) {
console.log(`Yes you have ${value}`)
}
}
}
}
new useString(['a', 'b', 'c']).print('b')
new useNumber([1, 2, 3]).print(2)
ジェネリクスを使う場合
class useAnyting<T> {
constructor(public arrays: T[]) {}
print(value: T): void {
for(const array of this.arrays) {
if(array === value) {
console.log(`Yes you have ${value}`)
}
}
}
}
new useAnyting<string>(['a', 'b', 'c']).print('b')
new useAnyting<number>([1, 2, 3]).print(2)
必ず存在するかわからないメソッドを使う場合
ジェネリクスで使うことで、型ごとに同じような機能を作る必要がない例は見ていただいたと思いますが、もし未来に定義する型にメソッドがあるかどうかわからない例だとエラーになってしまいます。
class Student {
print(): void {
console.log('This is Student')
}
}
class Teacher {
print(): void {
console.log('This is Teacher')
}
}
interface People {
print(): void
}
function print<T>(arr: T[]) {
for(let i = 0; i < arr.length; i++) {
// arrが必ずprintメソッドを持つ訳ではないので、エラーになる
console.log(arr[i].print)
}
}
print([new Student(), new Teacher])
ジェネリクスの引数をprintメソッドを持つインターフェイスで継承すれば、引数に渡る値はインターフェイスを満たす必要があり、必ずprintメソッドを持つことが確約されるためエラーにはなりません。
class Student {
print(): void {
console.log('This is Student')
}
}
class Teacher {
print(): void {
console.log('This is Teacher')
}
}
interface People {
print(): void
}
// Peopleインターフェイスを継承する
// 引数にprintメソッドを持っている値が渡される限りエラーにならない
function print<T extends People>(arr: T[]) {
for(let i = 0; i < arr.length; i++) {
console.log(arr[i].print)
}
}
print([new Student(), new Teacher])