TypeScriptメモ

ここでは`TypeScript`の基本的な構文や気になったところをメモしていきます。
React学習のための通過点のため`React`でよく使う部分を中心にメモしていく。

# TypeScriptとは
`TypeScript`はマイクロソフトがオープンソースで公開している`JavaScript`を拡張する形で設計されてたプログラミング言語のこと。
特徴としては以下が挙げられる。
* JavaScriptとは異なり型付けの言語であること
* コンパイルによるエラー検出やコードの安全性の向上
* JavaScriptの上位互換であること

また`TypeScript`はビルドツールを使って最終的に`JavaScript`のコードに変換（トランスパイル）されるため、`JavaScript`で実装したものとパフォーマンスの差異はない。

# 型の定義
変数や引数名の後ろに`:型名`をつける。これを型注釈といい、この型情報を変数や引数に付与することで値を制限する。
ただ、`TypeScript`には型推論の機能が備わっているため、**型の定義をしなかった場合自動的に型が決定される。**

## 変数
変数の宣言には`var``let``const`を用いる。
>`var`:再宣言や再代入が可能
`let`:再代入ができて再宣言ができない
`const`:再宣言と再代入ができない

:::note warn
`var`は関数スコープであり、`let`はブロックスコープであるため**影響が少ない`let`を使うのが好ましい。**
:::
```TypeScript:入力構文
var 変数:型 = 値
let 変数:型 = 値
const 変数:型 = 値
```

```TypeScript:入力例
var num: number = 1000
let flug: boolean = true
const age: string = '21'
```

## 配列
配列に型を指定するには構成する型と`[]`表記を用いる。

```typescript
const array; string[] = []
// arrayにString型の要素を追加
array.push('Hello')

// arrayにnumber型の要素を追加
array.push(1)
// 配列の型と異なるためコンパイルエラーになる。
```

## オブジェクト型
オブジェクトは`Key`と`value`によるデータ形式のインスタンスで、`TypeScript`では`key`の後に`:型`として型を定義する。

```typescript:入力構文
var 変数:{キー名:型; キー名:型; ...} = オブジェクト
let 変数:{キー名:型; キー名:型; ...} = オブジェクト
const 変数:{キー名:型; キー名:型; ...} = オブジェクト
```
```typescript:入力例
// オブジェクトの宣言
const user:{name:string; age: number} = {
    name:'taro'
    age:21
}
// 値の出力
console.log(user.name)
// taro
console.log(user.age)
// 21
```

オブジェクトの型はプロパティに`?`をつけることでプロパティを省略することができる。
```typescript
function printName(obj; {firstName:string; lastName?:string}){

}

// どちらもコンパイル時にエラーは出ない
printName({firstName:'taro'})
printName({firstName:'taro';lastName:'sato'})
```

## any
`any`は全ての型を許容する型のこと。
```typescript
let u:any = 32
// 他の型に代入してもエラーが起きない。
let n:string = u
```
`any`を利用すると**型チェックの機能が動作しなくなるため`TypeScript`の恩恵がなくなるので基本的には`any`は使わない。**

## 関数
`TypeScript`の関数では引数と戻り値に型を定義できる。
```typescript:構文
function 関数名(引数:型,引数:型,...):戻り値の型{
    // 関数の処理
}
```
```typescript:関数定義の例
// 関数の定義
function sayName(name:string):string{
    return `Hello ${name}`
}

// 
sayName('taro')
```
また引数に`?`をつけることで引数をオプション扱いにできる。
```typescript:関数定義の例
// 関数の定義
function sayName(name:string,greeting:string):string{
    return `${greeting} ${name}`
}

// 
sayName('taro') // taro
sayName('taro','Hello') // Hello taro
```
引数定義の際にデフォルト値を指定することで、引数を指定しない場合値がセットされる。
```typescript:関数定義の例
// 関数の定義
function sayName(name:string,greeting:string = 'Hello'):string{
    return `${greeting} ${name}`
}

// 
sayName('taro') // Hello taro
sayName('taro','Hey') // Hey taro
```

引数や戻り値には関数も指定できる。
```typescript:関数を引数に入れる場合

function printName(firstName:string,fomatter:(name:sting) => string){
    console.log(fommatter(firstName))
}

// 名前に「さん」をつける関数を定義
function formatName(name:string):string{
    return `${name}さん`
}

printName('taro')
// taroさん

```
`printName`関数の引数である`fomatter`では**引数が`string`型である`name`の戻り値が`string`型である関数を示している。
実際`fomatName`関数は引数が`string`で戻り値が`string`の関数で、引数の条件を満たしている。

### アロー関数
アロー関数の場合の型指定は以下の通りです。
```typescript:アロー関数の構文
(引数名:引数の型):戻り値の型 => JavaScriptの式
```

```typescript:アロー関数の例
let sayHello = (name:string):string => `Hello ${name}`
```

# 基本的な型の機能
## 型アサーション
型アサーションでは既に決定済みの型を上書きする機能のこと。
`TypeScript`上でDOMを扱うとき`HTMLElement`（もしくはnull）として扱われます。この時`HTMLElement`でも`div`なのか`canvas`なのかは`TypeScript`上では判断できない。
開発者側が型を知っている場合、この型アサーション機能を使って具体的な型に上書きすることができる。

対象となる型よりも具体的になる型、または汎化させる型に変更する場合に型アサーションを使う。

```typescript:型アサーション例
変数 = 値 as 型
```

```typescript:使用例
// main_canvasというidを持つElementを取得し、HTMLCanvasElementという型に上書きしている。（元はHTMLElement）
const myCanvas = document.getElemetById('main_canvas') as HTMLCanvasElement
```

## 型エイリアス
型エイリアスは型の定義に別の名前をつけることができる機能のこと。
この名前を参照して同じ方を複数回利用することができる。

```typescript:型エイリアス例
type 型名 = 型
```
:::note
型名は大文字始まりにすることが一般的
:::
```typescript:使用例１
// stringをNameという名前でも使えるようにしている。
type Name = string
```
```typescript:使用例２
// number型のx,yのプロパティを持つPointという型エイリアスを定義
type Point = {
    x:number;
    y:number;
}

// Point型を引数にとる関数の定義
function printPoint(point:Point){
    // Point型のxプロパティを出力
    console.log(point.x)
    // Point型のyプロパティを出力
    console.log(point.y)
}
```
:::note
関数自体の型も型エイリアスで定義可能
:::

```typescript:関数の型エイリアス例
// 引数がstringで戻り値がstringの関数
type Formatter = (a: string) => string
```

:::note
オブジェクトのキー名を明記しなくても型エイリアスは定義可能
:::
```typescript:
// オブジェクトのkeyをstring、valueをstringの型をLabelという型名にしている
type Label = {
    [key:string]: string
}

// 型エイリアスで定義した方でオブジェクトを作成
const labal:Label = {
    topTitle:'トップページのタイトル',
    topSubTitle:'トップページのサブタイトル',
    topFeature:'トップページの機能'
}
```

## インターフェイス
インターフェイスは型エイリアスと同じく型を定義する機能のこと。
型エイリアスよりも拡張性があり、クラスと一緒に用いることが多い。

```typescript:定義方法
interface 型名{
    プロパティ1:型1;
    プロパティ2:型2;
    // ...
}
```
```typescript:使用例（オブジェクト型）
interface Point{
    x:number;
    y:number
}
```
:::note
型エイリアスと異なり情報を追加できる
:::
```typescript:プロパティの追加
interface Point{
    x:number;
    y:number
}
// プロパティの追加
interface Point{
    z:number;
}

```

:::note
インターフェースを他のインターフェースに継承することもできる
:::

```typescript:
// Colorful型を宣言
interface Colorful{
    color:string;
}

// Circle型を宣言
interface Circle{
    radius:number;
}

// Colorful型とCircle型を継承してColorfulCircle型を宣言
// Colorful型とCircle型のどちらも使える
interface ColorfulCircle extends Colorful, Circle{}

const cc: ColorfulCircle = {
    color: '赤',
    radius: 10
}
```

## クラス
クラスの定義方法は以下の通り。
`JavaScript`のクラス記法に型が宣言できる機能が追加されただけ。
```typescript:クラスの定義方法
class クラス名{
    フィールド1:型1;
    フィールド2:型2;
    //...
    
    メソッド名(変数:型名){
        //メソッドの処理
    }
}
```
:::note
戻り値がない場合は`void`を型名にする
:::
```typescript:クラスの定義例
class Point{
    // フィールドの定義
    x:number;
    y:number;
    
    // コンストラクタメソッドの定義
    constructor(x:number = 0, y:number = 0){
        this.x = x
        this.y = y
    }
    // moveXメソッドの定義
    moveX(n:number):void{
    this.x +=n
    }
    
    // moveYメソッドの定義
    moveY(n:number):void{
    this.y +=n
    }
}

const point = new Point()
point.moveX(10)
console.log(`${point.x}, ${point.y}`)// 10, 0
```
:::note
クラスも`extends`を使って別のクラスを**継承**することができる
:::

### アクセス修飾子
:::note
アクセス修飾子でメソッドやフィールドのアクセスの範囲を制御することができる
:::

|アクセス修飾子  |説明  |
|:-:|:-:|
|宣言なし  |どこからでもアクセス可能  |
|public  |どこからでもアクセス可能|
|private |自身のクラスからのみアクセス可能|
|protected|自身のクラスとサブクラスからアクセス可能|

アクセス修飾子はフィールドの前に付与する。
```typescript:アクセス修飾子付与
class BasePoint{
    public x: number;
    private y: number;
    protected z: number;
}
```

# 開発で重要な型
## Enum型
:::note
`Enum型`は列挙型と呼ばれており、関連する定数まとめて定義することができる。
:::
```typescript:Enum型定義
enum 型名 = {
    定数名１,
    定数名２,
    //...
}
```
```typescript:Enum型定義例
// Enum型のDirectionを定義
enum Direction = {
    Up,
    Down,
    Left,
    Right
}

//Direction型を参照
let direction: Direction = Direction.Left
// 2という数字が出力される
console.log(direction)
```
`Enum型`は定義された順番に０からインクリメントされた数字が代入される。
もちろん定数なので値を指定することもできる。

```typescript:Enum型の定義例
enum Direction = {
    Up = 'UP',
    Down = 'DOWN',
    Left = 'LEFT',
    Right = 'LIGHT'
}
```

:::note
定数をまとめたオブジェクトを型として扱うことができるためバグやエラー解決が行いやすい。
:::



## ジェネリック型
:::note
ジェネリック型はクラスや関数で使う型を外部から指定できるようにして、クラスや関数に型の汎用性を持たせる機能のこと
:::
型を引数に入れるイメージ。
```typescript:ジェネリック型入力例
// クラスの定義
class クラス名<型変数>{
    //クラスの処理
}

// クラスの呼び出し
const インスタンス名 = new クラス名<型名>()
```
型変数を`<>`で囲むことで外部から型を指定できる。

```typescript:ジェネリック型実装例
class Queue<T>{
    // T型の配列の初期化
    private array: T[] = []
    // 配列にT型の要素を追加
    push(item: T):void{
        this.array.push(item)
    }
    
    // 配列の一番初めの値を取り出す
    pop():T | nudefined{
        return this.array.shift()
    }
}

// Queueクラスの呼び出し
// number型を指定
const queue = new Queue<number>()
queue.push(111)
queue.push(112)
// number型ではなのでエラー
queue.push('hogehoge')
```
:::note
Reactコンポーネントもジェネリック型のクラスで定義されており、コンポーネントが受け取る`props`の型を外部から定義することができる。
:::
## Union型・intersection型
:::note
`|`や`&`を用いて複数の型を組み合わせた型のこと。
変数や戻り値、型エイリアスに対して指定可能。
:::
`|`は話集合を表す`Union型`、`&`は積集合を表す`intersection型`のこと。
`intersection型`はそれぞれのデータ型がマージされた型として扱う。

```typescript:Union型使用例
// 変数の場合
let example : string | number

// 関数の引数に指定する場合
function printId(id: string | number){
    console.log(id)
}

// 型エイリアスに使う場合
type Id = string | number

function printId(id: Id){
    console.log(id)
}
```

```typescript:intersection型使用例
type　Contact = {
    name: string;
    email: string;
    phone; string
}

type Identity = {
    id: number | string;
    name: string;
}

// intersection型
type Employee = Identity & Contact
// Employeeの中身
`
{
    id: number | string;
    name: string;
    email: string;
    phone; string;
}
`
```


## リテラル型
:::note
リテラル型は決まった文字列や数値を`|`で区切り扱えるデータを制御することができる型。
:::
```typescript:リテラル型の記入例
変数: 許可するデータ1 | 許可するデータ2
```
```typescript:リテラル型例
let postStatus: 'draft' | 'published' | 'deleted'
postStatus = 'draft' // 許可されたデータなのでOK
postStatus = 'aaaa'  //許可されていないデータなのでエラー
```
:::note warn 
リテラル型で指定できるデータは論理型、数値型、文字列型である。
:::


## never型
:::note
`never型`は何も値を持たない型。
:::
`never型`には何も値を代入することはできないが、唯一`never型`だけ代入できる。

例外や終了しない関数の戻り値が`never型`となる。
戻り値がない`void型`との違いは`undefine`が代入できない点にある。


# TypeScriptのテクニック

## Optional Chaining


`?`をプロパティに入れることでプロパティが`Undefined`or`null`となり、値が返ってこなくても処理が行える。

```typescript:
// nullになりうるプロパティsocialを定義
interface user{
    name: string
    social?:{
        facebook: boolean
        twitter: boolean
    }
    
}

// User型のuserを宣言
let user: User 

user = {
    name:"taro",
    social: {
        facebook: true
        twitter: true
    }
}

console.log(user.social?.facebook)
// true

// socialプロパティを定義しないuserを定義
user = {
    name: "taro"
}

console.log(user.social?.facebook)
// 定義してないがエラーは起きない

```

:::note warn
`Undefined`や`null`のまま処理が進む可能性があるためあんま使わない方がいい。
:::

## Non-null Assersion Operator
明示的に型が`null`ではないことを示すことができる機能。
```typescript:
// userがnullの場合（引数に入れない）コンパイルエラーになるが、!(Non-null Assertion)を使うことでコンパイルエラーを抑制できる。
function processUser(str:string, user?:User){
    let str = 'taro'
    let s = user!.name
}
```



:::note warn
実際は型を事前に定義した方がいいためあんまり使わない。
型アサーションで型を用意することが多い。
:::

`Non-null Assertion`が良くない場合
```typescript:TypeScript
let myString = "Hello": String | null
// toUpperCaseは全部大文字にするメソッド
console.log(myString!.toUpperCase())
```
`JavaScript`にトランスパイルすると以下のようになる。
```javascript:JavaScript
let myString = "Hello"
console.log(myString.toUpperCase())
```
この場合`myString`が`null`になった時`myString.toUpperCase()`でエラーが出る。

## 型ガード
`if`や`switch`文の条件分岐に型チェックを行った場合、条件分岐のブロック移行は変数の型を絞り込む機能のこと。

:::note
変数の値の型を推論してくれる機能。
:::

```typescript:型ガード例
let x: string | number = "Hello"
// typeofで型ガード
// これによりstringの場合のみの処理を分けることができる
if(typeof x === 'string'){
    // 以降xはstringとして扱われる。
    console.log(x.toUpperCase())
}
```

## keyofオペレーター

:::note
型に対して、その型持つプロパティを`Union型`で返す。
:::


オブジェクトにあるキーを使用して何かの関数を処理を行いたい場合に安全に実装できる。

```typescript:
// interfaceの定義
interface User{
    name: string
    age: number
    email: string
}
type UserKey = keyof User
// 'name' | 'age' | 'email'
```


## インデックス型
オブジェクトのプロパティが可変のとき、プロパティの型をまとめて定義できる

```typescript:インデックス型例
// number型のプロパティを設定可能
type SupportVersions = {
    [env: number] = boolean
}

// string型のプロパティはエラーが起きる
let versions: SupportVersions = {
    102: false
    103: false
    // Stringだとエラー
    "104": true
}
```

:::note warn
そもそも可変のデータはあまり好まれないのであまり使わない
:::



## readonly
型エイリアス、インターフェース、クラスにおけるプロパティに`readonly`を指定すると、変更不可となる。
```typescript:
type User = {
    readonly name: string
    gender: string
} 
let user: User = {
    name: "taro"
    gender: "male"
}

// nameプロパティの変更はできない
user.name = "jiro"
```

:::note
`as const`で型アサーションすれば全プロパティに`readonly`が適応される。
:::

## unknown型
どのような値にも代入できる型。`any`との違いは任意の関数やプロパティにアクセスできない。

:::note
`unknown型`はそもそも関数やメソッド、プロパティを持たないからアクセスもクソもない。
:::
そのため関数やプロパティにアクセスするときには`typeof`や`instanceof`を用いて型安全な状況を作り実行する。
```typescript:
// nuknown型を定義
const x: unknown = 123
const y: unknown = 'Hello'

//　メソッドにはアクセスできない
console.log(x.toFixed(1))

// x自体はunknownだが値がnumberだからts側で型推論を行う（型ガード）
if(typeof x === 'number'){
    // 以降xをnumber型として扱う
    console.log(x.toFixed(1))
}
```


## 非同期のAsync/Await
TypeScriptでの非同期処理の書き方は以下の通り。
`Async Await`は簡単にいうと非同期処理を同期処理っぽくするやつ。

参考記事

https://qiita.com/cheez921/items/41b744e4e002b966391a

```typescript
// 非同期関数の定義
function fetchFromServer(id: string):Promise<{success: boolean}>{
    return new Promise(resolve => {
        setTimeout(() => {
            resolve({success: true})
            },100)
        })
}

async function asyncFunc(): Promise<string>{
    const result = await fetchFromServer('111')
    return `The result: ${result.success}`
}

(async ()=> {
    const result = await asyncFunc()
    console.log(result)
})()

asyncFunc().then(result => console.log(result))
```

## 型定義ファイル
:::note
`@types/ライブラリ名`で`JavaScript`ライブラリに型情報を付与することができる。

:::

```typescript:iQueryをインストールする場合
$ npm install --save-dev @types/jquery 
```

# TypeScriptの開発時設定
## tsconfig.json
`tsconfig.json`はコンパイルする際に必要なオプションを記述するためのファイルのこと。
詳しくは以下

https://typescriptbook.jp/reference/tsconfig/tsconfig.json-settings

## Prettier
スペースやインデントの数を指定したり、ダブルクォートをシングルクォートに揃えたりと、コードのフォーマットを指定することができる。
特に複数人で開発するときにこのファイルを共有してコードフォーマットを揃えてコンフリクトを防ぐことができる。

:::note
`Prettier`はコードを整形するためのツール
:::

```:Prettierのインストール
$ npm install prettier --save-dev
```

作成できたら`.prettierrc`というファイルを作成して、そのファイル内にコードフォーマットの設定値を記載する。

```:.prettierrc中身例
{
    // コードの末尾にセミコロンを入れるか
    "semi":false,
    // オブジェクト定義の最後のカンマを無しにするか
    "trailingComma":"none",
    // 文字列の定義などのクォートにシングルクォートを使用するか
    "singleQuote":true,
    // 行を開業する際の文字列の指定
    "printWidth":80
}
```

そのほかのオプションについては以下公式ドキュメント参考

https://prettier.io/docs/en/options.html

`Prettier`の適応には`package.json`の`scripts`の項目に以下を追加

```:package.json
{
    "scripts":{
        "prettier-format":"prettier --config .prettierrc 'src/**/*.ts' --write"
    }
}
```

以下のコマンドを実行することで`TypeScript`のソースコードに対してフォーマットが実行される。

```:
$ npx run prettier-format
```

## ESLint
:::note
`JavaScript`や`TypeScript`のコードを解析し、問題がある箇所を指摘するツール。
:::

`Prettier`と併用して使うことが多い。

`.eslintrc.js`というファイルで設定を記述する。

## コンパイルオプション
### noImplictAny
:::note
`noImplictAny`は型定義がない場合コンパイルエラーを出すためのオプション
:::

`TypeScript`から`JavaScript`にトランスパイルする際型定義がないと`any`として扱われる。`any`はエラーの原因なので、トランスパイルする過程で定義された`any`を検出して、エラーを吐かせるようにする方がいい。

### strictNullChecks
:::note
`strictNullChecks`は明示的に`null`や`undefined`を指定しない場合にエラーを吐くオプション
:::

`Union`型などで、`null`や`undefined`を明示することでエラーを防げる。

### target
:::note
`target`オプションはどのバージョンの`ECMAScript`で出力するのかを指定できるオプション
:::

**※`ECMAScript`は`JavaScript`の標準規格のこと。**

```:
$ tsc --target es5 sample.ts
```
このようにすることで`sample.ts`を`ECMAScript 5`準拠の`JavaScript`にトランスパイルすることができる。