TypeScriptを使ってみた

TypeScriptに興味を持ったのは、業務上でAngularを使っているからだ。

自分でもプライベートでAngularを使ってみると、やっぱりTypeScriptを一通り体系的にやっておいた方がよいと感じた。


# この記事に書くこと書かないこと
個別の型については特に書こうとは思わない。公式ドキュメントを見るとかの方がよい。

TypeScriptを使うと何が楽になるとかは書きたい。

flowについては使ったことも調べたこともない。Babelもよくわかってません。。

# 参考書籍

公式ドキュメントをやってもよいとは思ったけど、手軽な薄い本をやって、やった感を感じたいということで探してみたら下記の本が良書だった。

[Getting Started with TypeScript: Includes Introduction to Angular](https://www.amazon.co.jp/Getting-Started-TypeScript-Introduction-Angular-ebook/dp/B01MULZ5FA/)


# TypeScriptとは

TypeScriptはJavaScriptのsuperset。(JavaScriptをTypeScriptの部分集合たらしめたい、ということ？)

TypeScriptは最新のJavaScriptでの記述を可能にし、コンパイルして素のJavaScriptに記述し直す。

JavaScriptはECMA(European Computer Manufacturers Association)を標準化したもの。

ECMAScriptとかESとか言われるのはそのため。

標準化は西暦に対応していたが、`ECMAScript 4`は存在しない。また、2015からは番号が西暦に対応した。

`ES6`と言った場合、それは`ES2015`を指している。

```
1997 - ECMAScript 1
1998 - ECMAScript 2
1999 - ECMAScript 3
2009 - ECMAScript 5
2015 - ECMAScript 2015 (ES6)
2016 - ECMAScript 2016 (ES7)
2017 - ECMAScript 2017 (ES8)
```

本家のドキュメントは多分これとか。
- https://www.ecma-international.org/ecma-262/9.0/index.html
- https://github.com/tc39

実際、TypeScriptをコンパイルしなおして様々なバージョンのJavaScriptに記述し直すというのは簡単ですごーいと思った。

Babelとか理解不足＆設定がなんか難しそうというイメージを持っていたから、簡単にコンパイルし直すことができた、というのは大きかった。


# TypeScriptを使って嬉しいところ
- 最新のJavaScriptの機能を使ってもブラウザがサポートする古いJavaScriptに簡単にコンパイルできる。
- 型システムを使ってコンパイル時エラーが出る幸せ
    - 巨大なコードになった時、実行してみないとエラーがあるかどうかわからないのはツライ
- IDE等のサポート(コード補完とか)を受けることができる。
    - 自分はWebStrom使ってるけどVSCode使いも当然幸せになれる。
- 既存のTypeScriptでないコード資産も取り込むことができる。
    - まだ自分の理解不足なところもあるけれど、仕組みはあるようです。


# 使ってみる

## 基本的な使い方

TypeScriptをインストールすると`tsc`コマンドと`tscserver`がインストールされる。

```
$ npm install -g typescript
```

サンプルコードを書く

下記のようなコードを書いて`sample.ts`として保存する。tsはtypescriptの拡張子。

```javascript
function printFriend(friend) {
    console.log(friend.name)
}

printFriend({name: "taro"})
printFriend({firstName: "jiro"})
```

このコードは実のところTypeScriptの恩恵は何も受けていない。`$ tsc sample.ts`コマンドを実行するとコンパイルが実行されてsample.jsが生成される。

生成されたファイルをnode.jsなどで実行すると下記のような結果が出力される。

大きなコードになってくるとundefinedと出力されて初めて修正すべき点に気づくこともある。

```
$ node sample.js
taro
undefined
``` 

TypeScriptの恩恵に預かるには型を使う必要がある。先ほどのコードを下記のように直す。

```
interface Friend {
    name: string
}

function printFriend(friend: Friend) {
    console.log(friend.name)
}

printFriend({name: "taro"})
printFriend({firstName: "jiro"})
```

tscコマンドでコンパイルするとコンパイルエラーが出る。

interfaceとしてnameというプロパティを持つべきだが存在しないのでエラーになった。

IDEを使っているとコンパイルするまでもなくエラー表記される。

```
$ tsc sample.ts 
sample.ts:10:14 - error TS2345: Argument of type '{ firstName: string; }' is not assignable to parameter of type 'Friend'.
  Object literal may only specify known properties, and 'firstName' does not exist in type 'Friend'.

10 printFriend({firstName: "jiro"})
```

## tsconfig.json と tsc -w コマンド

`$ tsc -init`すると`tsconfig.json`が生成される。tscコマンドを実行した時のオプション設定をこのファイルから読みこんでくれる。

targetに`es5`と書いており、ここを変更することで様々なバージョンのJavaScriptにコンパイルしなおすことができる。

最新のJavaScriptの機能を各バージョンでどう記述し直すのかを見るのは割と面白い。


```typescript
interface Friend {
    name: string
}

let friends: Friend[] = [
    {name: "taro"},
    {name: "jiro"}
]

for (let friend of friends) {
    console.log(friend.name)
}
```

上記のようなTypeScriptを`tsc -t ES3 sample.ts`でコンパイルする。(tscコマンドはtsconfig.jsonがなければデフォルトでES3でコンパイルする。)

結果は下記のようなjsになる。for of記法は従来のJavaScriptの記法に戻っている。

```javascript
var friends = [
    { name: "taro" },
    { name: "jiro" }
];
for (var _i = 0, friends_1 = friends; _i < friends_1.length; _i++) {
    var friend = friends_1[_i];
    console.log(friend.name);
}
```

これを`tsc -t ES6 sample.ts`でコンパイルするとJavaScriptは下記のようになる。

```javascript
let friends = [
    { name: "taro" },
    { name: "jiro" }
];
for (let friend of friends) {
    console.log(friend.name);
}
```

何度もtscコマンドを実行するのが面倒な場合は`tsc -w`でウォッチモードにすることができる。

ファイルの変更に応じて自動でコンパイルしなおしてくれる。

## 既存のライブラリをTypeScriptの世界に引き込む

### 素のJSのライブラリを使う
npmからモジュールをダウンロードして使いたい場合があって、TypeScriptでない場合は少し面倒が起こる。

サンプルで書いたものをnpmに上げといた。[@abcb2/sugo-i](https://www.npmjs.com/package/@abcb2/sugo-i)


バージョンは`0.0.1`と`0.1.0`があって、素のJSのものとTypeScript化したバージョンのものだ。

main.tsに下記のような記述をする。

```typescript
import { printFriend } from '@abcb2/sugo-i'

let friend = {firstName: "太郎"}
printFriend(friend)
```

npmで下記をインストールする。

```
npm install @abcb2/sugo-i@0.0.1
```

tsconfig.jsonはtsc -initしたデフォルトのものを使ってtscコマンドでコンパイルする。

そうすると下記のようなエラーが出る。

```
 Could not find a declaration file for module '@abcb2/sugo-i'. '/home/kw/WebstormProjects/learn_es6/typescript-getstart/11_declaration_files/sample/node_modules/@abcb2/sugo-i/index.js' implicitly has an 'any' type.
  Try `npm install @types/abcb2__sugo-i` if it exists or add a new declaration (.d.ts) file containing `declare module 'abcb2__sugo-i';`

1 import { printFriend } from '@abcb2/sugo-i'
```

要は純粋なJavaScriptの記述なのでTypeScriptの世界で扱うための型の解決ができませんというエラー。

printFriend関数の中では引数のオブジェクトのfirstNameというプロパティを使うことが前提となっているが、それを解決する機構が@abcb2/sugo-i@0.0.1には存在しない。

### TypeScript化したライブラリを使う その1

`npm install @abcb2/sugo-i@0.1.0` でインストールする。

違いとしてはライブラリをTypeScript化したということだ。

tsconfig.jsonを修正してdeclarationファイルを出力してもいる。

こうすることでライブラリを使用する側でエラーは出なくなり、IDE上でもサポートしてもらえる。

自分が最初からTypeScript前提でライブラリを作る分には以上のアプローチでよいのだが、既存のライブラリで特段TypeScript化されていないものはどうすればよいのか。

先のエラーメッセージにも出ていたが、`@types`スコープのモジュールをインストールするというのが解決方法だ。

### TypeScript化したライブラリを使う その2

lodashという関数型ライブラリがある。

lodashをtypescriptで扱うためには`@types/lodash`をインストールすればよい。

main.tsを書く。

```typescript
import { range } from 'lodash'


let chapters: number[] = range(1, 12)
for (let num in chapters) {
    console.log(num)
}
```

このままtscコマンドでコンパイルすると`@types/lodash`をインストールしろとエラーがでるのでそのまま従う。

`npm install --save-dev @types/lodash`で後は正常に動作する。


どんな`@types`があるかは下記で検索できる。
- https://microsoft.github.io/TypeSearch/


# おわりに
既存のモジュールをTypeScript内で使おうとすると、自分の経験ではもうちょっと解決しなければいけないエラーだったりが出てきたりして苦戦したこともある。

tsconfigの設定を緩やかにして見なかったことにしたりしてしまったものもある。

TypeScriptは伊達にJavaScriptのsupersetを名乗っていない。とても良いものだと思った。

[origin](https://abcb2.github.io/blog/2018/08/04/get_start_typescript/)