TypeScriptに入門してみる

前回前々回とSvelteについての記事を書きました。

せっかくなのでTypeScriptも併せて導入してみようということでまとめてみようと思います。

TypeScriptとは

TypeScriptMicrosoftが開発したプログラミング言語です。

いわゆるAltJSと言われるプログラミング言語群の一つで、コンパイルすると生のJavaScriptが生成されます。

JavaScriptは動的型付け言語であるため、実行時に型が決まります。逆に言うと実装時は型を気にせずに書けてしまいます。

これは予期せぬ挙動に繋がりかねない大きな欠点です。言語によっては暗黙の型変換が行われたりして意図しない結果が出力されることもあります。

JavaScriptのこうした欠点を補完するのがTypeScriptです。つまり静的型付けを行うことができ、型安全性を保つことができます。

誤った型を代入しようとするとコンパイル時にエラーが出るため、バグを未然に防ぐことができます。

近年は静的型付け言語の方が人気が高い印象です。

TypeScriptのGithubを見てみてもスター数は86.8kとその人気の高さが伺えます。

TypeScriptの型について

ここからはTypeScriptにおける型についてまとめていきます。

プリミティブ型

JavaScriptにおけるプリミティブ型もそのまま使えます。型が異なる変数を代入することはできません。

const hoge: string = 'hogehoge';

const fuga: number = hoge; // Type 'string' is not assignable to type 'number'.

ちなみに上記の変数hogeno-inferrable-typesをONにしているとエラーになります。

型推論できるものは省略しましょうということみたいです。

リテラル

以下のように書くとどうなるでしょうか。

const hoge: 'fuga' = 'fuga';

こうすると変数hogefuga型となり、fuga以外の型が入れられません。このような型をリテラル型と言います。

なので↓はNGです。

const hoge: 'fuga' = 'piyo'; // Type '"piyo"' is not assignable to type '"fuga"'.

リテラル型はプリミティブ型の部分型なので対応するプリミティブ型の変数に代入できます。

const hoge: 'fuga' = 'fuga';
const piyo: string = hoge;

オブジェクト型

オブジェクトを型に指定することもできます。

type hoge = {
  fuga: string,
  piyo: number
};

const hogehoge: hoge = {
  fuga: 'fugafuga',
  piyo: 10
};

const hogefuga: hoge = {
  fuga: 100, // Type 'number' is not assignable to type 'string'.
  piyo: 10
};

const fugapiyo: hoge = {
  bohe: 'bohebohe', // Type '{ bohe: string; paya: string; }' is not assignable to type 'hoge'.
  paya: 100
};

オブジェクト型を定義するには上記のようにtypeを使う方法とinterfaceを使う方法があります。

interface hoge {
  fuga: string,
  piyo: number
}

interfaceはオブジェクトとクラスの型のみ定義できますが、typeはそれ以外の型も定義できます。

調べてみるとinterfaceを使った例の方が多そうですが、個人的にはオブジェクト型であればどちらでも良い気がします。

詳しい解説はこちらの記事がわかりやすかったです。

interfaceとtypeの違い、そして何を使うべきかについて

クラス型

先程クラスの話が少し出てきたのでクラス型にも触れたいと思います。

JavaScriptではES2015から他のオブジェクト指向言語と同様にクラスが使えるようになりましたが、TypeScriptではクラスを定義すると同時に型も定義されます。

class Hoge {
  fuga(): string {
    return 'fugafuga';
  }
}

こうするとstring型を返す関数fugaを持つHogeというクラス型が定義されます。

ちなみに以下のようなことも可能です。

interface Hoge {
  fuga: () => string;
}

class Hogehoge {
  fuga(): string {
    return 'fugafuga';
  }
}

const hogeClass: Hoge = new Hogehoge();

変数hogeClassinterfaceで定義したHogeというオブジェクト型になります。Hoge型はstring型を返すfugaという名前の関数型のプロパティを持つオブジェクト型です。

ここでHogehogeというクラスはstring型を返すfugaという関数を持つため、Hogehoge型とHoge型は同じです。

そのため、Hoge型の変数にHogehogeクラスのインスタンスを代入することができます。

ユニオン型

TypeScriptにはユニオン型と呼ばれる型があります。以下のようなものです。

type hoge = string | number;

hoge型にはstring型またはnumber型の値が入ります。

先程のリテラル型もユニオン型にすることができます。

type hoge = 'fuga' | 'piyo';

こうするとhoge型の変数にはfugapiyoしか入りません。Enum型に近いものになります。

(TypeScriptにもEnum型はあるのですがあまり推奨されないようです。詳しくはこちら

タプル型

タプル型は(厳密な理解ではないですが)要素の型と要素数が指定された配列型と理解しています。以下のように書きます。

type hoge = [string, number, boolean];

const fuga: hoge = ['fugafuga', 100, true];

const piyo: hoge = [1, 2, false]; // Type 'number' is not assignable to type 'string'.

例えばPromise.all()で複数の値が返ってくる関数の返り値の型に使ったりできます。

他には配列の要素数が決まっている場合等にも使えそうです。

タプル型は可変長にすることができ、

type hoge = [string, ...string[]];

const fuga: hoge = ['fuga', 'fugafuga']; // OK

const piyo: hoge = ['piyo']; // OK

const poyo: hoge = []; // NG

const bohe: hoge = [1]; // NG

const mohe: hoge = ['mohe', 10]; // NG

hoge型はstring型の要素が最低1つ、残りの要素もすべてstring型で長さは可変長という型になります。

また以下のような書き方もできます。

type hoge = [string, string?];

const fuga: hoge = ['fuga', 'fugafuga']; // OK

const piyo: hoge = ['piyo']; // OK

const poyo: hoge = ['poyo', 'poyopoyo', 'poyopoyopoyo']; // NG

この場合、2つ目の要素はオプションであっても無くてもOKですが、先程の可変長と異なり3つ目の要素を入れようとするとエラーになります。

まとめ

以上、TypeScriptの型についてざっくりまとめてみました。

まだ全てを理解して使いこなすには程遠いですが少しずつ入門していきたいと思います。

ちなみに以下のサイト・記事がわかりやすかったので参考にしました。

TypeScript入門『サバイバルTypeScript』〜実務で使うなら最低限ここだけはおさえておきたいこと〜

TypeScriptの型入門 - Qiita