【型のあるJavaScript】TypeScriptとは?入門編(メリットや環境構築方法など)
目次
1. TypeScriptとは
TypeScriptはMicrosoftが開発した言語で、JavaScriptの文法に型情報を追加したJavaScriptの上位互換的言語です。
TypeScriptの実行にはコンパイラーが必要で、手元のツールでJavaScriptに事前に変換して、ブラウザ上ではすでにコンパイル済みのJavaScriptを利用します。
2. TypeScriptを利用するメリット
JavaScriptをTypeScriptで開発するメリットがあるのでしょうか?
2-1. 思わぬミスを防止できる
基本的にJavaScriptには型の概念がありません。そのため文字列を代入した変数に対して後から数字を代入することが可能です。そのゆるさが JavaScript の魅力でもあるのですが、かえってそれがバグの原因にもなったりします。
TypeScript を正しく使えばそのようなミスは起こりにくいのでコードの質が向上します。
2-2. エディターの恩恵を受けられる
また、TypeScript のような型のある言語をつかうことで IDE(統合開発環境)やエディターの恩恵を受けることができます。
関数に対してどういう型で引数を渡せばいいのか、エディター側がGUI(グラフィカルユーザーインターフェイス)で提示してくれたりします。
以下は Visual Studio Code というエディターを利用したサンプルのソースコードの例です。間違った型が定義されたり利用されたりしている場合も、エディターが実行前に教えてくれます。
このようにコードを実行する前にすでにエディター上で実行エラーになることが確認できるので、開発スピードの向上も望めます。
チーム開発では、型定義がしっかりされていれば関数にどのような変数を代入すればいいか説明なしにわかるので、より TypeScript は効果を発揮するでしょう。
3. TypeScriptで開発するデメリット
TypeScriptで開発するデメリットはあるのでしょうか?
個人的にはチームや個人の開発レベルが高い場合はほとんどないと考えています。ただし、TypeScript の型は完全ではないので、変数に対して、全ての型を許容する any
型を宣言してしまったりすると、いくらでも型チェックをゆるくすることができます。
そうなると TypeScript を使うメリットがほとんどないどころか、返って開発効率を下げることになりかねません。また、逆にTypeScriptの上級者がチーム内にいると、そのひとのコードがあまりにも抽象度が高すぎて何が書かれているかチームメンバーが理解できないといったこともあります。
TypeScript を使うと品質のいいコードがかけるわけではなく、結局開発陣の意思疎通やルールの徹底に寄るところが大きいです。
4. TypeScriptの環境を用意しよう
では、TypeScript の魅力がわかったところで、次に JavaScript にコンパイルするための環境を整備しましょう。
TypeScript の環境を用意するには、Node.js の環境が必要です。
Node.jsのダウンロード
まずは Node.js というツールをダウンロードします。
Node.js とはもともとサーバーサイドで JavaScript を動かすためのツールなのですが、昨今ではgulp や Webpack などフロントエンドを便利に開発するためのツールとしても使われるようになりました。
上記のURLから Node.js をダウンロードしていただきダウンロードしたパッケージを解凍するだけでインストールが始まります。Node.js の導入は非常に簡単です。
TypeScriptのダウンロード
Node.js をインストールすると、npm コマンドが利用できるようになります。npm コマンドを使うことで必要なツールをダウンロードできます。
ダウンロード前に必ず、任意のディレクトリーを作成し、npm init -y
とターミナルに入力し、package.json を作成しておく必要があります。その後、以下のように TypeScript をインストールできるようになります。
$ npm install typescript --save-dev
応用)モジュールパンドラーWebpackと合わせて利用する
TypeScript を使うなら Webpack と組み合わせて使うのがいいでしょう。
Webpack とは様々な形式のファイル、JavaScript や CSS、また png, jpeg などの画像ファイルなどをモジュールとして扱い、JavaScript ファイルにまとめる(bundleする)ためのツールでモジュールバンドラーとも呼ばれています。
まとめられたリソース、CSSや画像ファイルなどは、JavaScript からアクセスが可能になります。
npm install
したツールは node_modules
というディレクトリーに入るのですが、 webpack
はそれらのツールも解釈しバンドルします。以下のソースコードは import
構文で jQuery と cssを読み込んで利用するサンプルコードになります。
import $ from "jquery"; import './style.css'; $(".hoge").addClass("active");
なお、jQueryは npm install jquery
--``save
で npmからダウンロードすることが可能です。
Webpack, ts-loader のインストール
Webpack で TypeScript を利用するには、以下のコマンドで webpack
webpack-cli
webpack-dev-server
ts-loader
をインストールします。
$ npm install webpack webpack-cli webpack-dev-server ts-loader --save-dev
webpack.config.jsの編集
次に、package.jsonと同じ階層にwebpack.config.jsを作成し編集します。
webpack.config.js とは様々ファイル形式をどのように解釈してバンドルするかを定義するためのファイルです。
以下の例では、拡張子が ts
もしくは tsx
のファイル名を ts-loader
(TypeScript を解釈して JavaScriptモジュールに変換するツール)を使って解釈させ JavaScript にコンパイルさせるサンプルコードになります。
const path = require('path'); module.exports = { mode: "production", entry: { 'backpax': './src/index', }, resolve: { extensions: ['.ts', '.tsx', '.js'] }, module: { rules: [{ test: /\.tsx?$/, exclude: /node_modules/, loader: 'ts-loader' }] } };
package.jsonの編集
package.json には scripts という項目がありますのでその項目を書き換えます。scriptsに書いたコマンドはそれぞれターミナルにコマンドを入力する際のエイリアスになります。
たとえばターミナルで npm run build
と入力するとnpmが代わりにターミナル上で、 webpack --config webpack.prod.js
というコマンドを実行してくれます。長いコマンドはこのようにscriptsに登録しておくと便利です。
"scripts": { "build": "webpack --config webpack.prod.js", "dev": "webpack-dev-server --config webpack.dev.js" }
tsconfig.jsonの編集
次に tsconfig.json を同じ階層に作成し編集します。tsconfig.json とは TypeScript のルールを記述するためのファイルです。
{ "compilerOptions": { "target": "es5", "module": "es6", "moduleResolution": "node", "outDir": "./lib", "sourceMap": true, "declaration": true, "strict": true, "lib": [ "es2018", "dom" ] }, "include": [ "src" ], "exclude": [ "node_modules" ] }
ここでは詳しい説明は省略しますが、tsconfig.json の諸々の設定はこちらのページに記載されています。
https://www.typescriptlang.org/docs/handbook/tsconfig-json.html
5. TypeScriptの文法解説
つぎに、TypeScriptの文法を解説していきます。
5-1. 変数に対する型宣言
TypeScriptには primitive
な型として、文字列を取り扱う string
型、数字を取り扱う number
型、true, falseを取り扱う boolean
型があります。
簡単な例で行くと、TypeScriptでは以下のように変数に対して型を定義できます。
const name: string = 'hanako';
ただし、TypeScriptには推論機能があるので、この場合、型宣言は省略できます。
const name = 'hanako';
特に効果を発揮するのは変数がObjectの場合です。最初に型を定義しておけば定義した型と実際に変数の型が違っていた場合はエラーになります。
以下の例では Person 型を定義しています。Person 型で定義されたオブジェクトには name
プロパティに string
型を、ageプロパティにnumber
型を authorized
プロパティに boolean
型をsexプロパティに ’man’ または ’woman’ という文字列を代入する必要があります。
type Person = { name: string; age: number; authorized: boolean, sex: 'man' | 'woman' }; const person: Person = { name: 'hanako', age: 24, authorized: false, sex: 'woman' }
クイズ Q1
ではここでクイズです。答えはこの記事の第7章で解説します。黄色の蛍光ペンで塗られた箇所で型エラーが起こっています。Person
型を修正して、エラーを解消してください。
type Person = { name: string; age: number; authorized: boolean; sex: 'man' | 'woman'; bloodType: 'a' | 'b' | 'ab'; }; const person : Person = { name: 'hanako', age: 24, authorized: false, sex: 'woman' bloodType: 'o' }
2. 関数の型宣言
関数の引数や返り値に対しても型を定義することができます。
以下は数字の配列を受け取ってその平均値を返すサンプルになります。関数の引数は数字の配列のため型は number[]
、返り値は数字のため number
になります。
function getAvg(items: number[]):number { const total = items.reduce((sum, item) => { return sum + item; }, 0); return total / items.length; } const avg = getAvg([1,2,3,4,5]);
クイズ Q2
ではここでクイズです。答えはこの記事の第7章で解説します。下の isSameFavorite
関数は二つのオブジェクトを比較して true
false
を返す関数です。黄色の蛍光ペンで塗られた箇所で型エラーが起こっています。Person 型を修正して、エラーを解消してください。
type Character = { name: string; favorite: string; }; type Person { name: string; age: number; authorized: boolean; sex: 'man' | 'woman'; } function isSameFavorite (personA: Person, personB: Person) { if (personA.favorite === personB.favorite) { return true; } return false; } const taro = { name: 'taro', bloodType: 'a' } const hanako = { name: 'hanako', bloodType: 'o' } isSameFavorite(taro, hanako);
3.(応用)ジェネリクスをマスターしよう
また、TypeScript で重要な概念としてジェネリクスという機能があります。これは、いわば型の関数的機能で型を引数として受け取って新たな型を返します。
以下は配列から条件と一致する要素を返す簡単な関数です。みたことのない、 T という記述がありますが、これがジェネリクスと言われる部分で、ここに代入される型に応じて他の部分の型などが変わってきます。
function find<T>(query: T, items: T[]): T | null { for ( item of items) { if ( query === item ) { return item } } return null } const item = find(5, [1,2,3,4,5]); find('5', [1,2,3,4,5]); //この場合は型エラーになる。
今回のケースでは、find 関数の query に number が型として代入されているので、T=number
となり、以下のように定義されたのと同義になります。
function find(query: number, items: number[]): number | null { for ( item of items) { if ( query === item ) { return item } } return null }
最初は難しいかもしれませんが、ジェネリクスとは <> の中に好きな型を代入することで新たな型定義を生成するためのもので型の抽象度をあげることができます。
クイズ Q3
以下は filter
関数である配列から、第二引数で定義した条件にあう要素以外を取り除いて配列を返す関数である。
いま、any
型が3つ定義されているので、これをT
をつかって同じ型が配列に入っていることを保証してください。また、func の中に入ってくる引数は第一引数の配列の要素に一致するようにしてください。
function filter<T>(items: any, func:(item: any) => boolean): any { const results = []; for (let i = 0; i < items.length; i++) { const item = items[i]; if (func(item) === true) { results.push(item); } } return results; } const items = filter(['taro', 'hanako', 'fuga'], (item) => item.indexOf('t') !== -1); // [taro] const items = filter(['taro', 'hanako', 3], (item) => item.indexOf('t') !== -1); // 配列の中に文字列と数字が混在しているためエラー
6. Visual Studio CodeでTypeScriptを開発しよう
6-1. Visual Studio Code とは
Microsoft が開発しているエディターで現在、無償のソフトの中では一番利用されています。Microsof tは TypesScript の開発元でもあるので、Visual Studio Codeと TypeScript は非常に相性がよく型チェックや補完などを自動で行ってくれます。
ここではどのように使いなせばいいかを事例と合わせて紹介します。
まずはこちらのページより Visual Studio Code をインストールしましょう。
https://code.visualstudio.com/
インストールが終わったら Visual Studio Code で TypeScript を体験してみましょう。Visual Studio Code では最初から TypeScript の構文をチェックする機能が入っているため、インストール後は特にプラグインをインストールしたりしなくてもエディターでエラーチェックや補完が機能します。
まず、projectsフォルダを任意の場所に作成し、それを Visual Studio Code で開いてみましょう。Visual Studio Code を開き以下の画像のように「Open Folder…」と書かれた文字をクリックすることによって作成したフォルダーを開くことができます。
フォルダーを開いた後は、テストようにファイルを作成しましょう。ここでは、variable.tsという名前で作成します。
6-2. 型エラーチェック機能
まず、Visual Studio Code でエラーチェックが機能していることを確認します。
以下のように記述してみましょう。
const name: number = 'hoge';
変数 name
は number
型なのに、文字列(string
)が代入されているためエラーになり赤い波線が表示されるのがわかります。
6-3. 型定義へ移動する
次に先ほどと同じ手順で、definition.ts を作成しましょう。そこに以下のように記述します。
export type Person = { name: string; age: number; sex: 'man' | 'woman' }
2で作成した、variable.ts にもどり、一番最初の行にカーソルを移動し、以下のように記述します。
import { Person } from './definition'; const person: Person = { name: 'hanako', age: 24, sex: 'woman' }
Person にカーソルを合わせると以下のように型定義がポップアップで表示されることが確認できます。
6-4. メソッドの型情報表示
以下のように TypeScript ではオブジェクトのメソッドの引数とその帰り値を定義することができます。
type Person = { name: string; age: number; sex: 'man' | 'woman' isSex(sex: 'man' | 'woman'): boolean; }
Visual Studio Code では変数側でも以下の画像のように定義したメソッドの引数と帰り値をホバーすることで確認することができます。
6-5. プロパティの一括置換
地味に便利な機能として、型のプロパティを変更したくなった際にはそれに紐づく変数のプロパティもすべて一括で置換してれる機能があります。関連する変数を自分で探して書き変える必要がなくなり、手間も無くなり保守性も向上します。
一括置換したい項目上で右クリックすると、メニューが表示されるので下の画像のように Rename Symbol
をクリックすると、変更したいプロパティの入力が求められるので入力します。すると、その型に関連する全ての変数のプロパティ名が変更されているのが確認できます。
7. 解説
クイズ Q1解説
変数 person
には bloodType
に 文字列 o
が代入されていますが、型 Person
の bloodType
には a
,b
,ab
だけが代入可能な状態になっています。なので以下のように型 Person
の bloodType
に o
を追加します。このように |
で区切って2つ以上の型を許容する型をユニオン型といいます。ユニオン型を利用することで、string | number
と宣言し、数字または文字列を許可する型も定義することができます。
type Person = { name: string; age: number; authorized: boolean; sex: 'man' | 'woman'; bloodType: 'a' | 'b' | 'ab' | 'o'; }; const person : Person = { name: 'hanako', age: 24, authorized: false, sex: 'woman' bloodType: 'o' }
クイズ Q2解説
isSameFavorite
の第一引数、第二引数にはともに Person
型が代入可能になっていますが、Person
型には favorite
プロパティが存在しないのでエラーになっています。この場合は favorite
プロパティを持つ Character
型を使いましょう。
type Character = { name: string; favorite: string; }; type Person { name: string; age: number; authorized: boolean; sex: 'man' | 'woman'; } function isSameFavorite (personA: Character, personB: Character) { if (personA.favorite === personB.favorite) { return true; } return false; } const taro = { name: 'taro', bloodType: 'a' } const hanako = { name: 'hanako', bloodType: 'o' } isSameFavorite(taro, hanako);
クイズ Q3 解説
filter
関数の第一引数の配列の要素の型と、第二引数のfuncの引数が一致する必要があるので、itemsの型は T[]
、itemの型は T
になります。また戻り値も items
の T[]
と同じ型になります。
function filter<T>(items: T[], func:(item: T) => boolean): T[] { const results = []; for (let i = 0; i < items.length; i++) { const item = items[i]; if (func(item) === true) { results.push(item); } } return results; } const items = filter(['taro', 'hanako', 'fuga'], (item) => item.indexOf('t') !== -1); // ['taro'] const items = filter(['taro', 'hanako', 3], (item) => item.indexOf('t') !== -1); // 配列の中に文字列と数字が混在しているためエラー
8. まとめ
TypeScript を使うことで型チェックをブラウザでの実行前にエディター上でおこなうことができるので、コードの品質を向上させることができます。
さらに同時にエディターの補完機能により、スピードの向上も図ることができます。チーム開発では変数や関数に対してどのような型をいれればいいか口頭で説明しなくてもいいので大いに貢献するでしょう。
TypeScriptには今回紹介しきれなかった便利な機能がたくさんあるのでぜひこの機会に導入を検討してみてください。