【TypeScriptの魅力を語る】優秀な型推論が「つよいシステム」を作るのだ
こんにちは。ICTRADの松原です。木造の賃貸一戸建てに住んでいます。備え付けのエアコンが壊れてしまい、寒さに悩まされる今日後この頃です。
今回は私が普段業務で使用しているTypeScriptという言語の魅力について語りたいと思います。
まずはWikipediaから
TypeScript はマイクロソフトによって開発され、メンテナンスされているフリーでオープンソースのプログラミング言語である。TypeScriptはJavaScriptに対して、省略も可能な静的型付けとクラスベースオブジェクト指向を加えた厳密なスーパーセットとなっている。C#のリードアーキテクトであり、DelphiとTurbo Pascalの開発者でもあるアンダース・ヘルスバーグがTypeScriptの開発に関わっている。TypeScriptはクライアントサイド、あるいはサーバサイド (Node.js) で実行されるJavaScriptアプリケーションの開発に利用できる。
出典:https://ja.wikipedia.org/wiki/TypeScript
冒頭からWikipediaのコピペになりますが、ここで抑えておきたいポイントとしては「TypeScriptがJavaScriptのスーパーセット(上位互換)である」、つまりJavaScriptとして書かれたコードはTypeScript上でも動くということです。これは既存のJavaScriptで書かれたプログラムを段階的にTypeScriptに移行することを可能にしています。前提知識としてはJavaScript(ES2015〜)とNode.js(npm)の使い方を抑えておけば基本的に問題無いと思います。
それでは、TypeScriptの魅力をつらつらと書き綴っていきたいと思います。
JavaScript自体の魅力
自分は2015年頃からJavaScriptに触れているのですが、ES2015(ES6)以降次々と新しい構文やメソッドが増えていき、今ではかなりモダンな言語に仕上がってきたなと感じています。テンプレートリテラル、アロー関数、分割代入、スプレッド構文、async / await、ES Modules(import / export)、Optional Chainingなどなど、今まで実装されてきた機能はどれも一度使ったら手放せないものが多いですね。
段々と魅力的になってきたJavaScriptですが、型システムに関しては粗雑な作りとなっており、度々ネタにされていたりします。その弱みを補強するのがTypeScriptです。
静的型付け言語
変数や関数の返り値などの型がプログラムの実行前に決まっている言語を静的型付け言語と言います。Wikipediaの説明にもありましたが、TypeScriptは静的型付け言語に分類されます。対して、TypeScriptのサブセットであるJavaScriptはプログラムの実行時に型が決定される動的型付け言語に分類されます。
静的型付けのメリットとして、型の不整合などのエラーをプログラムの実行前に検知することができ、タイポやデグレの混入を減らせる点や、エディタの補完の恩恵を受けやすい点があげられます。エンジニアも人間なので、頻繁にミスをします。その時にすぐ横で叱ってくれる人(コンパイラ)がいることは非常にありがたいです。
const greet = (name: string) => `Hello ${name}`
greet(42) // Argument of type 'number' is not assignable to parameter of type 'string'.(2345)
豊富な型の表現・操作
TypeScriptはエンジニアが表現したい物事の型を、多様な表現でサポートしてくれます。number、string、booleanなどのプリミティブの型はもちろんのこと、
・42/"hoge"/falseといった特定の値のみを表現するリテラル型
・個数の決まった配列を表現するタプル
・AまたはBを表現するユニオン型
・AかつBを表現する交差型
・型をパラメータとして扱えるGeneric型
・ある型から別の型を生成するMapped Types
・文字列を展開できるTemplate Literal Types
など様々な型があります。特にTemplate Literal Typesはバージョン4.1で搭載され、Twitter界隈でも多くの盛り上がりを見せていました。
// number型
type MyPrimitive = string
// リテラル型(特定の数値)
type MyLiteral = 42
// タプル(個数の決まった配列)
type MyTuple = [number, string]
// ユニオン型(OR)
type MyUnion = 'yes' | 'no'
// 交差型(AND)
type MyIntersection = { name: string } & { age: number }
// Generic型(型のパラメータ化)
type MyGeneric<T> = { prop: T }
type HasStringProp = MyGeneric<string>
// Mapped Types(型のmap操作)
type Base = { a: number, b: string, c: boolean }
type MyMapped = { [P in keyof Base]: string }
// Template Literal Types(文字列の展開)
type MyTemlateLiteral<T extends string> = `Hello ${T}`
type HelloWorld = MyTemlateLiteral<"World">
賢い型推論
静的型付け言語といえば型注釈を大量に書く必要があると思いがちですが、TypeScriptには優秀な型推論が備わっているので、色々なケースで明示的な型の宣言をサボることができます。
// 明示的な型注釈
// aはnumber型
const a: number = 42
// リテラルの代入における型推論
// bは42(リテラル型)
const b = 42
// オブジェクトの分割代入における型推論
// cはnumber型
const { c } = { c: 42 }
// 関数の戻り値の型推論
// dはnumber型
const f = (): number => 42
const d = f()
特に型のOR表現であるユニオン型と型推論のコンビはとても強力な表現を可能とします。ifやswitchなどを用いて型の「絞り込み」ができるのは妥当として、TypeScriptの型推論の優秀なところは、絞り込みの結果を記憶することで後続処理に可能性が排除された状態で型情報を渡せる点です。
この機能によって、エディタでの補完がコードの箇所によって限定されたり、論理的にありえないコードを書いてコンパイルがエラーを出し、トラブルを未然に回避することができるのです。
// 異なるプロパティを持つオブジェクトのユニオン型
type Hoge = {
kind: 'hoge',
hoge: number,
}
type Fuga = {
kind: 'fuga',
fuga: string,
}
type HogeOrFuga = Hoge | Fuga
// ユニオン型を引数に取り、独自のプロパティの値を返す関数
const getOriginalProperty = (target: HogeOrFuga) => {
if (target.kind === 'hoge') {
// ここではtargetはHoge型として扱われる
return target.hoge
}
// 上のif文とreturnでHoge型の可能性が消されているため、
// ここではtargetはFuga型として扱われる
return target.fuga
}
// 関数の返り値もreturn句からnumber | string型に推論される
const result = getOriginalProperty({ kind: 'hoge', hoge: 42 })
ReactやVueなどでも標準サポートされるように
少し前の話になりますが、2018年後期頃にscaffoldingツールのcreate-react-appやvue-cliでもTypeScriptが標準サポートされるようになり、SPA開発におけるTypeScript導入のハードルがグンと下がりました。後続のNext.jsやNuxt.jsなども当たり前にサポートしています。今では「TypeScriptとの親和性」というのも技術選定の1つの基準になってきていると感じています。
全てのライブラリに型を「DefinitelyTyped」
今でこそTypeScriptで書かれたライブラリも徐々に増えつつありますが、まだまだJavaScriptで書かれているライブラリがほとんどです。基本的にはTypeScriptには型情報が必要なのですが、多くのライブラリが型情報を持ち合わせていません。
そこで登場するのがDefinitelyTyped(@types)というライブラリの型情報の提供に特化したTypeScriptコミュニティ(GitHubリポジトリ)です。DefinitelyTypedのおかげで、既存のJavaScript資産をTypeScriptにコンバートすることなく活用でき、今のTypeScript開発に欠かせない存在となっています。
注意点として、あくまで自助的なコミュニティのため、必ずしも対応するライブラリの最新の状態が反映されているわけではないのですが、大抵のものが揃っている型情報のドンキホーテです(もちろん無料ですが)。OSSコントリビュートも比較的容易なので、チャンスがあれば狙ってみるといいかも...?
まとめ
色々とTypeScriptの魅力について書き連ねてみました。コンパイラによって人為的ミスによるエラーが減り、システムが堅牢になる。型があることによってコードの可読性が高まり、エディタの補完も上手く働いて生産性が上がる。そういったメリットはもちろんあるのですが、私が1番伝えたいのはTypeScriptを使った開発は楽しいということです。TypeScriptによってモダンな静的型がもたらされ、コードをジグゾーパズルとして組み合わせていくような感覚が得られるようになりました。書き捨てのスクリプトなどを除いて、もうJavaScriptには戻れません。
序盤でも書いたとおり、TypeScriptはJavaScriptのスーパーセットなので、現行のJavaScriptで書かれたプロジェクトにも少しずつ適用していくことができます。JavaScriptさえ知っていれば、あとはTypeScriptを始めるだけです。良い開発ライフを。
終わり
(ICTRAD・松原浩平)