Typescript型Tips集

## はじめに
Typescriptの型をより深く学んでいく際に重要かつ理解に少し時間のかかるようなものをまとめました。
Typescriptの型について初歩的な内容からさらに一歩進んで学びたい人は、後掲する参考記事と共に、この記事を参考にしていただけると嬉しいかぎりです。
ではいこう。　

## in
まず、Typescriptの型をより深く見始めた時によく目にする'in'。
こいつは何も複雑なことはやっていないので理解はそこまで難しくはないが、異なるニュアンスの使われ方をするので、どう言った意味で使われているのかの判別が必要。

### 使用例1
inはあるオブジェクトが指定されたプロパティを持っているかをbooleanで返す。

```typescript
type HumanInfo = {
 name: string
 age: number
 isMarried: boolean
}

console.log("name" in HumanInfo) // true
console.log("address" in HumanInfo) // false
```
これは型の絞り込みの際にとても役に立つ。

```typescript
type FlowerInfo = {
 name: string
 age: number
 languageOfFlower: string
}

const validation = (value: HumanInfo| FlowerInfo) => {
 if("languageOfFlower" in value) {
  value.languageOfFlower.length === 0 ? "花言葉の入力は必須です。" : undefined
 }
}
```
### 使用例2
こちらの使用例の方が頻出だろう。keyofと併用することでkeyofでreturnさせた直和型のオブジェクトをマッピングできる。

```typescript
type Flower = {
 name: string
 age: number
 languageOfFlower: string
}

type ReadonlyAll<T> = {
 readonly [t in keyof T]: T[t]
}

// type ReadonlyAll<Flower>は、
{
 readonly name: string
 readonly age: number
 readonly languageOfFlower: string 
}
```
Array.map(i => i + 1)のArrayがTでiがtみたいなイメージ。

## indexed access operator
Typescriptの型にはtuple型というものが存在する。あくまでも値じゃなくて型。

```typescript
type AutumnFlower = ['コスモス', 'パンジー', '金木犀']
const a: AutumnFlower = ['コスモス', 'パンジー', '銀木犀'] // コンパイルエラー
```
このtuple型は、値と同様にして、indexを指定してその番目の要素の型を取得できる

```typescript
type sample = ['str', 1000, true, () => 1]
sample[0] // string
sample[2] // boolean
```

さらにtuple[number]とするとそのtupleの要素の型が直和型で求められる。

```typescript
sample[number] = string | number | boolean | () => number
```

加えて、lengthメソッドを呼び出せる。要素数の型を取得できる

```typescript
sample['length'] = 4
```

## Conditional Type
独自の型を定義する時、その型を条件式によって動的に変更することができる。
書き方は三項演算子と似たような感じなので、特に難しい点はない。===とかではなくextendsを使う。
左辺が右辺の型を継承しているか、左辺が右辺に代入可能かを考えれば良い。

```typescript
type A<T> = T extends number ? T : never
const foo = 10
const bar = "10"
type Foo = A<typeof foo> // number
type Bar = A<typeof bar> // never
```
### Union Distribution
Conditional Typeのなかで、Union型（直和型）の型を扱う時、そのunion型は一つ一つがmapされて、評価される。
Typescript組み込みのExclude<T, U>の実装が非常にこの例としてわかりやすい。 
type Excludeはあるオブジェクトから指定された要素を除いた型を返すものである。

```typescript
type Exclude<T, U> = T extends U ? never : T;

type FlowerShop = {
 name: string
 owner: string
 handlingQuantity: number
 closeDay: string[]
}

type BasicShopInfo = Exclude<FlowerShop, 'closeDay' | 'handleQuantity'>

// BasicShopInfoは
{
 name: string
 owner: string
}
//のみ
```
この時、T extends U ? never : Tのところでは、

```typescript
{ 'name' extends 'closeDay' | 'handleQuantity' ? never : 'name' },
{ 'owner' extends 'closeDay' | 'handleQuantity' ? never : 'owner' },
{ 'handleQuantity' extends 'closeDay' | 'handleQuantity' ? never : 'handleQuantity' }...

```
 と言った感じでmapされて評価される。この性質がUnion Distribution。

### infer
inにつぐ謎word、'infer'。だが、理解はそんなに難しくなく、慣れると結構使えそうなやつ。Conditional Typeと組み合わせて使用する。
簡潔に説明すると、conditional typeの中で出現した型をキャプチャして再利用できる。途中で変数化しておける、みたいなイメージ。

```typescript
type PickParamType<T> = T extends (x: infer R) => any ? R : never
const foo = 'fooooooo!'
const bar = (x: number) => x + 1
type Foo = PickParamType<typeof foo> // never
type Bar = PickParamType<typeof bar> // number

// barは引数にnumberをとるfunctionであるから、評価式のinfer RのところでRにnumberが入る
```

## 交差型
先ほどから直和型やunion typeというワードがちらほら出てきたが、それと対をなすような概念が、この交差型である。
直和型が一つ一つの型を|でつないでいたのに対し、交差型は&でつなぐ。

```typescript
type Aspect = {
 color: string
 height: number
}
 
type Internal = {
 languageOfFlower: string
 kind: string
}

type Flower = Aspect & Internal // {color: string, height: number, languageOfFlower: string, kind: string}
```
### 注意点
&は上書きではないことに注意。どちらかというと制約の追加。

```typescript
type Foo = {a: string, b: boolean}
type Bar = Foo & {c: number} // {a: string, b: boolean, c: number}
type Zee = Foo & {a: string | () => any} // {a: string, b: boolean}であって{a: string | () => any, b: boolean}ではない
```

## 可変長引数
Typescriptでは引数の数を可変にしておくことができる。

```typescript
type Foo<T extends any[]> = T extends [first: any, ...rest: infer R] ? R : never 
type Bar = ['first', 2, true]
type Zee = Foo<Bar> // [2, true]
```
上記の例では可変とした引数の先頭要素の型を弾いた残りの型を返している。
でもどうやら逆は、同じやり方ではできないらしい。ぴえん🥺

```typescript
type Foo<T extends any[]> = T extends [...args: infer R, last:any] ? R : never // これはできない。 
```

## 終わりに
Typescriptの型について、いろいろ調べてチョットダケワカッタ気がした。（本当にチョットだけね。）
値の扱いに比べて抽象度が上がる分少し理解はしづらい面もあるが、じっくり考えればわからないことはない（普段実装しうる範囲内ではね）

## 参考
https://qiita.com/suin/items/25588b2beba7a3fcce4f
https://qiita.com/Quramy/items/e27a7756170d06bef22a
https://qiita.com/ryo2132/items/ce9e13899e45dcfaff9b
https://qiita.com/SotaSuzuki/items/efe32e07e52dce329653
https://qiita.com/uhyo/items/da21e2b3c10c8a03952f
https://qiita.com/uhyo/items/e2fdef2d3236b9bfe74a