C/Java/C#(SI系)なエンジニアに贈るTypeScript事始め その2（TypeScriptの”Type”の威力)

##前回までのまとめ
前回、無事にTypeScriptを動かす準備が出来、動作確認まで完了できました。
今回はTypeScriptが"Type"Scriptと呼ばれる中身を少しづつ確認していきたいと
思います。

##歴史を振り返る
Wikipediaを見てると、最初のリリース(ver 0.8)が2012年10月1日。2012年っていうと、
Windows Phoneがまだ頑張ってる頃ですね、、それ以外で言うと、、

- InstagramのAndroid版がリリースされたのが4/4
- FacebookがIPOしたのが5/17
- Teslaの最初のModel Sのデリバリーが6/5
- iPad mini 1st genが発表されたのが10/23
- Wii Uがリリースされたのが11月

って感じです。だいぶ大昔な気がしませんか？我々SIer的にはJava SE7が出たばっかりで
ヒャッホイしてた感じですね。まだ牧歌的な時代だったのかもしれません。
Lamdbaとか恥ずかしながらスミマセン、知りません！って頃でした。（私は）

##今回も始めていきましょう！

早速、TypeScriptの威力を確認していきます。
環境は前回のまま、進めていきます。

### "Type"の威力
```typescript:src/app.ts
let data1: string = 'hoge';
data1 = 'foo';
data1 = 100;
```

文字列型に文字列代入した後に数値を代入しています。これ、JavaScriptだと
普通に出来ちゃうんですが、TypeScriptだとそうは行きません。

```shell
> ./node_modules/.bin/tsc
src/app.ts:3:1 - error TS2322: Type '100' is not assignable to type 'string'.

3 data1 = 100;
  -----
Found 1 error.
```

コンパイルエラーです。
これは心理的にラク。宣言時に型を決めたらあとはコンパイラがちゃんと問題を拾ってくれます。
これが"Type"Scriptってことですね。でも、動的型付けな人たちからすると、「変数宣言時に
型書くのめんどくさい・・・」って思うんですよね、多分？でも大丈夫！型推論にもちゃんと
対応します。

### 型推論しつつも"Type"で縛る
```typescript:src/app.ts
let data2 = 'bar';
data2 = 'foo';
data2 = 100;
```
変数宣言時には型を指定していません。

```shell
> ./node_modules/.bin/tsc
src/app.ts:3:1 - error TS2322: Type '100' is not assignable to type 'string'.

3 data2 = 100;
  -----

Found 1 error.
```
それでもちゃんと拾ってくれます。型推論しながら、ちゃんと違う型が来たら検知してくれます。
だけど、カンペキではないんです。

### 初期値は忘れずに！

```typescript:src/app.ts
let data3;
data3 = 'foo';
data3 = 100;

console.log(data2);
```

今回、data3は初期化されていません。この状態でコンパイルすると、、、

```shell
> ./node_modules/.bin/tsc
>
```
え、、？エラー出ない、、、、そうなんです。型推論は「初期値からしか型を推論できない」のです。
型を省略して、初期値入れなかったら、もはやTypeScriptといえど、エラーを検出することは
出来ないのです。JavaとかC#なエンジニアは型を書くのを忘れることはないと思いますが、、
型はちゃんと書きましょうね！という話です笑

### any型は使ってはなりません
どういう目的で作ったのかわからなかったのですが、無敵の人、ならぬ無敵の型"any"があります。

```typescript:src/app.ts
let data2: any;
data2 = 'foo';
data2 = 100;
```

もう読んで字の如し笑。使ってはなりません。これを使わないと立ち行かない局面があるのなら
誰か教えて欲しいです（真面目に）。TypeScriptには「共用型」という型もあるのですが、
それについては長くなるのでまた今度笑。

ちなみに、Javaな人はやらないと思いますが、undefined, nullで初期化した場合もany型を
指定した場合と同じ挙動になるので、やっちゃダメです。多分。

```typescript:src/app.ts
let data2 = undefined;
data2 = 'foo';
data2 = 100;
```
コンパイルすると、、

```shell
> ./node_modules/.bin/tsc
>
```
あぁ、、型チェックが効きません。残念。

### コンパイルオプションをちゃんとする
TypeScriptではコンパイルオプションで型チェックを厳しくすることができます。

```json:tsconfig.json
{
  "compilerOptions": {
    "module": "commonjs",
    "noImplicitAny": true,
    "target": "es2017",
    "sourceMap": true,
    "types": ["node"]
  },
  "exclude": [
    "node_modules"
  ]
}
```
"noInplicitAny"をtrueに指定することにより「暗黙的なany型を許容しない」ことになります。
例えば、以下のコードの場合、コンパイルするとどうなるでしょうか、、？

```typescript:src/app.ts
//引数に型を明記していない！
function log(arg) {
    console.log(arg);
}

//初期値を指定せずに文字列代入してから数値代入！
let data2;
data2 = 'foo';
data2 = 100;
```

早速コンパイルしてみると、、、

```shell
> ./node_modules/.bin/tsc
src/app.ts:2:14 - error TS7006: Parameter 'arg' implicitly has an 'any' type.

2 function log(arg) {
               ~~~

Found 1 error.

```

おや、、？関数の仮引数には型チェックが効きますが、初期値を指定しない場合は
拾ってくれてませんね、、これは正しくは"noInplicitAny"オプションは
　**「後で代入された値に基づいて型が推論される」**からです。

こういうコード書くとちゃんとコンパイル時に拾ってくれます。
（変数data2に数値を代入してからcharAt()をコール。)

```typescript:src/app.ts
//引数に型を明記したよ。
function log(arg: String) {
    console.log(arg);
}

//初期値を指定せずに文字列代入してから数値代入！
let data2;
data2 = 'foo';
console.log(data2.charAt(0));
data2 = 100;
console.log(data2.charAt(0));
```

コンパイルすると、、

```shell
> ./node_modules/.bin/tsc
src/app.ts:11:19 - error TS2339: Property 'charAt' does not exist on type 'number'.

11 console.log(data2.charAt(0));
                     ~~~~~~

Found 1 error.
```

良いですね。型チェックに関するコンパイルオプションはたくさんあるので、また別途整理します笑。

##本日のまとめ
だいぶ長くなってきてしまったので本日はここまでにしたいと思います。書きたいことがいっぱい
あるのですが、毎回テーマを絞って勉強していきたいと思います。また次回、お付き合いください！

**本日の勉強結果**

- any型は使わないで〜！
- 初期値はなるべく設定しましょう
- コンパイルオプションには気をつけて。"noInplicitAny"はtrueでよろです