TypeScriptチートシート4（応用）

<h2>インターセクション型</h2>
・合併型(uniontypes):AかBどちらかの型を持つ
どちらかの型が成立すればOK

・交差型(intersection):AとB両方の型を持つ（共通する型ではない）
両方の型を満たせばOK

```typescript
type Engineer = {
  name: string;
  role: string;
};

type Blogger = {
  name: string;
  follower: number;
};

type EandB = Engineer & Blogger;
const suda: EandB = {
  name: "masaki",
  role: "front-end",
  follower: 10000,
};
```


<h2>Union 型の絞り込み</h2>

```typescript
interface Hoge {
  foo: string;
  bar: number;
}
interface Piyo {
  foo: number;
  baz: boolean;
}

type HogePiyo = Hoge | Piyo;

//オブジェクトにbarとbaz両方あるとどちらの型になるのかわからないので、参照できない

const obj: HogePiyo = {
  foo: 'hello',
  bar: 0,
  baz:false
};

//プロパティ 'bar' は型 'HogePiyo' に存在しません。
//プロパティ 'bar' は型 'Piyo' に存在しません。
const aaa = obj.bar
```


<h4>in</h4>
'bar' in objというのはbarというプロパティがobjに存在するならtrueを返し、そうでないならfalseを返す式
型を絞り込めば、正しく使える

```typescript

function useHogePiyo(obj: Hoge | Piyo): void {
  // ここではobjはHoge | Piyo型
  if ('bar' in obj) {
    // barプロパティがあるのはHoge型なのでここではobjはHoge型
    console.log('Hoge', obj.bar);
  } else {
    // barプロパティがないのでここではobjはPiyo型
    console.log('Piyo', obj.baz);
  }
}

```

<h4>Type guard</h4>
typeof演算子は与えられた値の型を文字列で返す演算子

```typescript

function func(value: string | number): number {
  if ('string' === typeof value) {
    // valueはstring型なのでlengthプロパティを見ることができる
    return value.length;
  } else {
    // valueはnumber型
    return value;
  }
}
```

<h4>instanceof</h4>

```typescript
class Dog {
  speak() {
    console.log("bow-bow");
  }
}

class Bird {
  speak() {
    console.log("tweet-tweet");
  }

  fly() {
    console.log("flutter");
  }
}

type Pet = Dog | Bird;
function havePet(pet: Pet) {
  pet.speak();
  if (pet instanceof Bird) {
    pet.fly();
  }
}

havePet(new Bird());

//コンソール
tweet-tweet
flutter

havePet(new Dog());

//コンソール
bow-bow

```

<h2>タグ付きユニオン</h2>
オブジェクトに対してユニオン型を使うときに役立つ

```typescript
//type: 'Some'
//type: 'None'
//というタグをつけておく
//このタグを使って、型を分ける

interface Some<T> {
  type: 'Some';
  value: T;
}
interface None {
  type: 'None';
}

type Option<T> = Some<T> | None;

//ifまたはswitchで場合分け

function map<T, U>(obj: Option<T>, f: (obj: T)=> U): Option<U> {
  if (obj.type === 'Some') {
    // ここではobjはSome<T>型
    return {
      type: 'Some',
      value: f(obj.value),
    };
  } else {
    return {
      type: 'None',
    };
  }
}

//上と下は同じ

function map<T, U>(obj: Option<T>, f: (obj: T)=> U): Option<U> {
  switch (obj.type) {
    case 'Some':
      return {
        type: 'Some',
        value: f(obj.value),
      };
    case 'None':
      return {
        type: 'None',
      };
  }
}
```

<h2>ユニオン型とインターセクション型を合わせて使ってみる</h2>

```typescript
interface Hoge {
    type: 'hoge';
    foo: string;
}
interface Piyo {
    type: 'piyo';
    bar: number;
}
interface Fuga {
    baz: boolean;
}

type Obj = (Hoge | Piyo) & Fuga;

function func(obj: Obj) {
    // objはFugaなのでbazを参照可能
    console.log(obj.baz);
    if (obj.type === 'hoge') {
        // ここではobjは Hoge & Fuga
        console.log(obj.foo);
    } else {
        // ここではobjはPiyo & Fuga
        console.log(obj.bar);
    }
}
```

<h2>型アサーション</h2>
手動で型を上書きする
as

```typescript
//今の状態の型
//const input: HTMLElement | null
const input = document.getElementById('input')
//input.valueなど使えない

const input = <HTMLInputElement>document.getElementById('input')
//または
const input = document.getElementById('input') as HTMLInputElement
input.value = 'value'
```

<h2>!</h2>
nullじゃないと言い切る方法
if文で条件分岐しなくてもよくなる

```typescript
//今の状態の型
//const input: HTMLElement
const input = document.getElementById('input')!
```

<h2>インデックスシグニチャ</h2>
・オブジェクトが複数のプロパティを持つ可能性を示す
・[key:T]:U　のように示す
・keyはstringかnumberのみ


```typescript
const capitals: {
    [countryName:string]:string
} = {
    japan: 'tokyo',
    korea: 'soul',
    //エラー
    age:23
}
```

<h2>関数のオーバーロード</h2>
戻り値の型を正しく伝える

```typescript
function toUpperCase(x: string | number): string | number {
  if (typeof x === "string") {
    return x.toUpperCase();
  }
  return x;
}

const upperHello = toUpperCase("hello")
//upperHelloの型は、const upperHello: string | number
//---------------------------------------------------

function toUpperCase(x: string ): string
function toUpperCase(x: number): number
function toUpperCase(x: string | number): string | number {
  if (typeof x === "string") {
    return x.toUpperCase();
  }
  return x;
}

//オーバーロードすることによって関数の戻り値の型を正しくできる
const upperHello = toUpperCase("hello")


```

<h2>Optional Chaining</h2>
プロパティが連結していくとき指定したプロパティがなければnullを返す

```typescript
//?はあってもなくてもいいプロパティ
interface DownLoadedData {
    id: number;
    user?: {
        name?: {
            first: string;
            last:　string
        }
    }
}

const downloadedData: DownLoadedData = {
    id:1
}

//エラー
//オブジェクトは 'undefined' である可能性があります
console.log(downloadedData.user.name)

//解決策の一つ
//userがあったらnameを返す
//なければundefinedを返す
console.log(downloadedData.user?.name)

```

<h2>Nullish Coalescing</h2>
userがundefined or nullの場合??の後ろを返す

```typescript
const userData = downloadedData.user ?? 'no-user'
```

<h2>LookUp型</h2>
オブジェクトの型のメンバが持っている型にアクセスする

```typescript
interface DownLoadedData {
  id: number;
  user: {
    name?: {
      first: string;
      last: string;
    };
  };
}

type id_1 = DownLoadedData['id']
//number
type id_2 = DownLoadedData['user']['name']
//type id_2 = {
//    first: string;
//    last: string;
//} | undefined
```

<h4>具体例</h4>

第一引数Tは{ foo: string; bar: number; }型
第二引数K extends keyof Tはfooまたはbarに限定する
戻り値T[K]は({ foo: string; bar: number; })['foo']型つまりstring

```typescript
function pick<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const obj = {
  foo: 'string',
  bar: 123,
};

const str: string = pick(obj, 'foo');
const num: number = pick(obj, 'bar');
// エラー: Argument of type '"baz"' is not assignable to parameter of type '"foo" | "bar"'.
pick(obj, 'baz');
```

<h2>constアサーション</h2>
as const をつけると変えられなくなる

```typescript
let milk = 'milk' as const
//let milk: "milk"

const array = [10,20]
//const array: number[]

const array = [10,20] as const
//const array = [10,20] as const



```