知識ゼロから始めるTypeScript 〜基本編〜

## はじめに

現在担当するプロジェクトでTypeScriptが使われていたので学習備忘録です。

- TypeScriptってよく聞くけどイマイチよく分からない
- 導入することで何が嬉しいの？

そんな疑問を解決したい。

TypeScriptシリーズ全4回連載中です:writing_hand:
[知識ゼロから始めるTypeScript 〜基本編〜](https://qiita.com/yukiji/items/52ea9106e254c1a12dea) :point_left:今ココ
[知識ゼロから始めるTypeScript 〜関数編〜](https://qiita.com/yukiji/items/614ff63ade4072731750)
[知識ゼロから始めるTypeScript 〜クラス編〜]
(https://qiita.com/yukiji/items/3db06601ece7f080b0d0)
[知識ゼロから始めるTypeScript 〜応用編〜]
(https://qiita.com/yukiji/items/e890053e62d2f7edc7c7)

## TypeScriptとは
- Javascriptに静的型付けの定義をすることで堅牢なプログラムを書くことが可能。
- 型を定義することによりプログラムを動かす前にエラーに気付くことが出来る。
- 型を定義することによりプログラムを動かす前に意図しない型が代入されるのを防ぐ。

## TypeScriptを導入する目的
- 型安全を確保してバグを減らすのが目的

## 実行環境
- MacOS Catalina 10.15.2
- VScode

## 使用するモジュールのインストール

```
$ npm i -D typescript ts-node ts-node-dev
```

`typescript`
typescript本体 

`ts-node`
.tsファイルをコンパイルして実行する

`ts-node-dev`
ts-nodeに依存している
実行したts-nodeをwatchしてくれる。

## package.jsonを編集

- 手軽に実行できるように`scripts`を登録しておく

```json:package.json
"scripts": {
    "dev": "ts-node-dev --respawn"
  }
```

## tsconfig.jsonを作成

```
$ npx tsc --init
```

`tsconfig.json`が生成される

細かな設定ができるが、今回はデフォルトの設定でOK

## 実行サンプル

```ts:sample.ts

export {}

let moji: string = "ハロー世界"

console.log(moji)
```

`export{}`=`モジュール化`するという意味。
モジュール化することにより、変数のスコープがグローバルになることを防ぐ。

上記の例は変数`moji`に`string型`の型宣言をしている

この場合、文字列(string型)しか受け付けず、string型以外はコンパイルエラーになる。

基本的には`変数名: 型`の様な書き方をする

### 実行してみる

```
$ npm run dev sample.ts

// ハロー世界
```

tsファイルの内容をJavaScriptにコンパイルして実行してくれる

一度実行するとファイルはウォッチされるので、ファイルを保存するたびに実行されるので便利


## 型一覧

### number型、string型
```ts

let year: number = 1969

let name: string = 'foo'
```

### boolean型

```ts
let isFinished: boolean = true
```

### array型

```ts
let numbers: number[] = [1, 2, 3]

//非推奨の書き方
let strings2: Array<string> = ['Tokyo', 'Nagoya']

//二次元配列の場合
let nijigenHairetsu: number[][] = [
  [50, 100],
  [150, 300]
]

//型が混在する場合
let hairetsu: (string | number | boolean)[] = [1, false, 'Japan']
```

### tuple型
- 配列の中身の0番目がstring型、2番目がnumber型などにしたい場合に使う

```ts
let profile: [string, number] = ['foo', 1969]
```

### any型

- あまり望ましくない形 (型推論ギブアップ宣言)
- APIなどでデータが返ってくる場合、そのデータの構造の予測はできないので、エンジニアが指定する必要がある。

```ts
import axios from 'axios'

let url: string = 'https://xxx'

axios.get(url).then(function(response) {
  let data: any
  // any型=どんな構造のオブジェクトが返ってくるか分からない
  data = response.data
})
```
### interface型
- オブジェクトの型を作ることが出来る

```ts
interface ObjectInterface {
  name: string
  age: number
}

let object: ObjectInterface = {
  name: 'foo',
  age: 26
}
```

先ほどの`any型`のAPIから返されるオブジェクトの構造がわかるときに使用してみる

```ts
import axios from 'axios'

export {}

let url: string = 'https://xxx'

axios.get(url).then(function(response) {
  interface Article {
    id: number
    title: string
    description: string
  }
  // APIから返されるデータ構造を指定
  let data: Article[]
  data = response.data
})
```

### void型
- 関数からの戻り値がないことを期待するような型
- 関数には必ずしも`return`があるわけでは無い。

```ts
function returnNoting(): void {
  console.log('I dont return anything')
}

console.log(returnNoting())
```

### null型、undefined型

- そのまんま

```ts

let absence: null = null

let data: undefined = undefined
```

### never型

- 例外を起こす関数や呼び元に戻ってこない関数の戻り値に対応する型

```ts
function error(message: string): never {
  throw new Error(message)
}
```

### object型
- オブジェクトの構造がわかっている場合は、各種プロパティに対しても型宣言する。

```ts
let profile2: { name: string, age: number } = { name: 'foo', age: 26 }
```

### 型エイリアス(Type Aliases)

- 一度作成した型を流用できる。

```ts

type Mojiretsu = string

const fooString: string = 'Hello'
const fooString2: Mojiretsu = 'Hello'

const example1 = {
  name: 'foo',
  age: 26
}

type Profile = {
  name: string,
  age: number
}

const example2: Profile = {
  name: 'buzz',
  age: 27
}

// example1のデータ構造を設定できる
type Profile2 = typeof example1
```

### unknown型
- ざっくり言うと型安全なany型
- any型を積極的に使っているとコンパイルエラーにはならないものの、実行時にエラーになってしまう。
- 一旦フワッと型定義したいときにany型に依存するのではなく暫定的にunknown型を定義する
- typeofを使ってある特定の型であることを確認しながら安全にコードを実行させる仕組みのことをタイプガードという

```js:unknown.ts
const kansu = (): number => 26

let numberUnknown: unknown = kansu()

// typeofを使って型を絞る
if (typeof numberUnknown === 'number') {
  let sumUnknown = numberUnknown + 10
}
```

### intersection型
- 既存の型を再利用して新たな型を作る

```ts
type Picher1 = {
  throwingSpeed: number
}

type Batter1 = {
  battingAverage: number
}

const Daimaou: Picher1 = {
  throwingSpeed: 154
}

const Ochiai: Batter1 = {
  battingAverage: 0.367
}

type TwoWayPlayer = Picher1 & Batter1

const Tsuneta: TwoWayPlayer = {
  throwingSpeed: 154,
  battingAverage: 0.367
}
```

### union型
- 型のor的な感じ
- 下の例だと`number型`か`string型`のどちらも対応した型宣言ができる

```ts
let value: number | string = 1
value = 'foo'
value = 100
```

### Literal型
- 変数に代入されるパターンを設定したい時
- 例えばstring型でも「この文字列しか受け付けない」様なパターンを作れる

```ts
let dayOfTheWeek: '日'|'月'|'火'|'水'|'木'|'金'|'土' = '日'
dayOfTheWeek = '月'

let month: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 = 1
month = 12
```

### enum型
- 数値や文字列を列挙するときに便利
- 自動的に番号を付与してくれる
- 数値は設定できる(初期値は0)

```ts
enum Months {
  //初期値は0なので1を設定
  January = 1,
  February,
  March,
}

console.log(Months.January)
// 1

enum COLORS {
  RED = '#FF0000',
  BLUE = '#0000FF'
}

let green = COLORS.GREEN
// 追加も可能
```


## 補足

### ワークスペースのtypescriptを使用する

VScodeにはデフォルトで`TypeScript`がインストールされている。

しかし、基本的にはプロジェクト毎に管理したいので`package.json`でインストールしたTypeScriptを使いたい。

①VScode右下のステータスバー`TypeScript`横の`数字(画像では3.8.3)`をクリックする
<img width="197" alt="スクリーンショット 2020-03-12 19.25.39.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335670/046defd4-e5ce-5d57-09fc-f9811b69a881.png">
②`ワークスペースのバージョンを使用`をクリック
<img width="599" alt="スクリーンショット 2020-03-12 19.25.49.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335670/9271e00e-d898-d5fa-7370-c70d4318a370.png">
これで完了。
大事な設定なのでしっかり行いたい。


### ts-nodeでエラーが出たとき

- パッケージのバージョン履歴を調べて一つずつ試していく。
- エラーが出ないバージョンをインストールして対処

```
$ npm info ts-node versions
```

```:モジュールの削除
npm uninstall -D ts-node
```

```:モジュールのインストール
npm uninstall -D ts-node@x.x.x
```

## まとめ

型宣言は変数に対して制約が強くなるような型を定義することが理想的。
型安全なコードを書いて堅牢なプログラムを書いていきたい:metal: