概要
前回の続き。
TypeScriptの機能
ジェネリクス
ジェネリクスとは「抽象化されたデータ型」を表現する機能です。
同じ様な関数だが、型違いの場合に、引数である型を抽象的に使う時に便利な機能です。
ジェネリクスを使わない例では、
let getStringArray = (value: string): string[] => {
return [value, value, value];
};
let getNumberArray = (value: number): number[] => {
return [value, value, value];
};
冗長ですよね。
関数の受け取る引数が違うだけなのに、複数の関数を定義している例です。
だめな解決策としては、型の部分をany型にするという解決策がありますが、それではTypeScriptで静的型付けをしている意味が全くありません。
そこで登場するのが、 ジェネリクス という機能です。
let getArray = <T>(value: T): T[] => {
return [value, value, value];
};
console.log(getArray<number>(3));
やっている事は単純で、<T>
という抽象的な型を宣言し、実行時に<T>
の部分に型を指定してあげるだけです。
今回の例で言えば、<T>
の部分にnumberが入り、関数内のT
の部分が全てnumberに置き換わります。
もちろん、numberの部分をstringに変更すれば、getArrayのT
の部分はstringになります。
また、T
の部分はどんな文字列でも可能ですが、慣習的にTypeのTを使う事が多いです。
今回は関数でのジェネリクスですが、ジェネリクスは型を指定するところならばどこでも利用可能です。
クラス・インタフェースでも利用可能です。
ジェネリクスの制約
インタフェースを利用する事で、ジェネリクスに制約を与える事も可能です。
まずは、クラスで制約の無い場合のジェネリクスです。
class MyData<T> {
constructor(public value: T) {}
getArray(): T[] {
return [this.value, this.value, this.value];
}
}
let v1 = new MyData<string>(`hello`);
console.log(v1.getArray());
let v2 = new MyData<number>(123);
console.log(v2.getArray());
クラス宣言のすぐ後ろに<T>
を持ってきて、クラス内でのT
をジェネリクス化しています。
これで、インスタンス化する際に<T>
の部分に型を持って来れば、インスタンス化時に指定した型が代入されるという仕組みです。
次に、制約を与えてみましょう。
どういう事かというと、「T
の型は何でも良いが、特定のプロパティを持っている型にしてね。」という事です。
という事は独自の型でないとだめですよね。
そこで登場するのが、前回登場したインタフェースです。
インタフェースで、特定のプロパティを持った型を作成し、その型が持っているプロパティがないとコンパイル時にエラーを出力するという事です。
interface Result {
a: number;
b: number;
}
class MyData<T extends Result> {
constructor(public value: T) {}
getArray(): T[] {
return [this.value, this.value, this.value];
}
}
let result = {
a: 3,
b: 5
};
let v1 = new MyData<Result>(result);
console.log(v1.getArray());
この様に、インタフェースを継承する形でT
に制約を与えます。
extendsされたT
はResult型のプロパティを持っていなければならないという制約を持ちます。
前回紹介した通り、TypeScirptのインタフェースは「構造的部分型」になっているので、上記の例で言えば、aとbというプロパティさえ持っていればOKです。
Result型でなくてもOKという事です。
終わり
4回に渡って、TypeScriptの主要な機能について解説してきました。
TypeScriptは型定義を持った、JavaScriptのスーパーセットであるという事が分かってもらえたかと思います。
しかも、TypeScriptは既存のJSの資産も利用する事ができます。
(Angular2, React, jQueryなど)
その際には、ただimportするだけでなく、型定義ファイルも取得しないといけません。
typingsという管理機能を使うのですが、また別途解説します。
(TypeScript2.0ではnpmのみで型定義ファイルを取得できるそうです。)
今まで、ES6 + Babelで開発していたプログラムも、TypeScriptを使って安全に、保守性高く開発していければと思います。