Typescriptの応用的な使い方 @yukilulu0229

## AかつBのように定義する

### type ```&```

```a.ts
type Engineer = {
  name: string,
  role: string
}

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

type EngineerBlogger = Engineer & Blogger

const yukilulu: EngineerBlogger = {
  name: "yukilulu",
  role: "engineer",
  follower: 1000
}
console.log(yukilulu)

```

```
Engineer & Blogger
```

```&```で繋げる

### interface ```extends```

```a.ts
interface Engineer  {
  name: string,
  role: string
}

interface Blogger  {
  name: string,
  follower: number
}

interface EngineerBlogger extends Engineer, Blogger {}

const yukilulu: EngineerBlogger = {
  name: "yukilulu",
  role: "engineer",
  follower: 1000
}
console.log(yukilulu)

```

```
nterface EngineerBlogger extends Engineer, Blogger {}
```

```extends```で繋げる

## 条件文で型を絞る

### ```typeof``` 型で指定する

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

```
string | number
```

number型は```toUpperCase()```のメソッドを使えないのでエラーになる

```
if (typeof x === "string") {
    return x.toUpperCase();
}
```
なので ```typeof```でstring型のみにif文で分岐して利用できるようにする

### ```in``` プロパティで指定する

```b.ts
interface Engineer  {
  name: string,
  role: string
}

interface Blogger  {
  name: string,
  follower: number
}

type NomadWorker = Engineer | Blogger
function describeProfile(nomadWorker: NomadWorker) {
  console.log(nomadWorker.name); // 共通のnameだけが現在表示できる

  if ("role" in nomadWorker) {
    console.log(nomadWorker.role) // roleを持っているEngineerの型に絞り込まれた
  }

  if ("follower" in nomadWorker) {
    console.log(nomadWorker.follower) // followerを持っているBloggerの型に絞り込まれた
  }
}

```

```
console.log(nomadWorker.name); // 共通のnameだけが現在表示できる
```
この時点ではEngineerかBloggerとどちらなの状態かわからないので共通のnameのみを使える

```
if ("role" in nomadWorker) {
  console.log(nomadWorker.role) // roleを持っているEngineerの型に絞り込まれた
}
```
```"role" in nomadWorker```をつかってroleを持っているEngineerの型に絞り込まれた

```
if ("follower" in nomadWorker) {
    console.log(nomadWorker.follower) // followerを持っているBloggerの型に絞り込まれた
}
```

```"follower" in nomadWorker```を使ってfollowerを持っているBloggerの型に絞り込まれた

### ```instanceof``` インスタンスで絞り込む

```c.ts
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(); // 共通のspeakはそのまま使える
  if (pet instanceof Bird) {
    pet.fly(); // petはBirdのインスタンスであるという指定しからBirdの型であると絞り込まれた
  }
}

havePet(new Bird());
```

```
pet.speak()
```

// 共通のspeakはそのまま使える

```
if (pet instanceof Bird) {
```

```pet instanceof Bird``` petはBirdのインスタンスであるという指定しからBirdの型であると絞り込まれた

## タグ付きユニオンで分岐させる ```kind```

リテラル型をうまく利用する

```kind:"dog" = "dog"```

```a.ts
class Dog {
  kind:"dog" = "dog"
  speak() {
    console.log("bow-bow");
  }
  sit() {
    console.log("sit")
  }
}

class Bird {
  kind:"bird" = "bird"
  speak() {
    console.log("tweet-tweet");
  }
  fly() {
    console.log("flutter");
  }
}

type Pet = Dog | Bird;

function havePet(pet: Pet) {
  pet.speak(); // 共通のspeakはそのまま使える
  switch (pet.kind) { // kindで分岐させる
    case "dog":
      pet.sit();
      break
    case "bird":
      pet.fly()
      break
  }
}

havePet(new Bird());
```

```
kind:"dog" = "dog"
```
```kind: "文字列a" = "文字列a"```でkindの中に入る文字列を固定する

```
switch (pet.kind) { // kindで分岐させる
    case "dog":
      pet.sit();
      break
    case "bird":
      pet.fly()
      break
}
```

それを```kind```を使ってswitch文で分岐させる

### interfaceでの書き方

```kind: "engineer"```

## 型アサーションを使って上書きする

```.ts
const input = document.getElementById("input");
input.value = "init input name";
```
そのままだと HTMLElementといういう型になって valueを使うことはできない


### 後ろに ```as``` をつける

Reactを使う場合を後ろにasをつけるほうがよい
理由はタグが紛らわしくなるから

```as.ts
const input = document.getElementById("input") as HTMLInputElement;
input.value = "init input name";
```

```as HTMLInputElement```を後ろにつけて ```HTMLInputElement```型だと上書きする

#### 変数を分けないで書く

```.ts
(document.getElementById("input") as  HTMLInputElement).value = "init value"
```

```()```で囲ってその後ろにプロパティをつける

### 先頭に ```<>``` をつける

```.ts
const input = <HTMLInputElement>document.getElementById("input");
input.value = "init input name";
```

```<HTMLInputElement>```をつけて ```HTMLInputElement```型とする

## 関数のオーバーロード

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

  return x;
}

const val = toUpperCaseFunc(3) // number型になっている
console.log(val)
const val2 = toUpperCaseFunc("hello") // string型になっている
console.log(val2)
```

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

  return x;
}

```

```function toUpperCaseFunc(x: string): string;``` 
```function toUpperCaseFunc(x: number): number; ```
それぞれを関数の上に書く
それによってしっかりした型で帰るようになる

## オプショナルチェイン ```?```

```.ts
interface DownloadData {
  id: number,
  user?: {
    name: string
  }
}

const downloadData:DownloadData = {
  id: 1
}
console.log(downloadData.user?.name)
```

```
user?: {
    name: string
}
```

```user```がundefinedであることを許容している場合

```
console.log(downloadData.user?.name)
```

```downloadData.user?.name```
```?```をつけることでなかったら```undefined```を返す あれば そのまま返すと設定することができる

## nullish coalecing ```??```

https://zenn.dev/lollipop_onl/articles/eoz-ts-ncoc-transpile#nullish-coalescing

```.ts
const userData = downloadData.user ?? "no-user"
```

```??```をつけることで undefined または null のときに 後ろのものが代入される

### 注意 OR演算子だと起こり得る事

```.ts
const userData2 = downloadData || "no-user"
```
```||``` これだと 0 や false というデータが入っていても ```"no-user"```が入ってしまうことに注意

## オブジェクトのメンバーの型を取得するlookup型

参考: 

https://commte.net/typescript-lookup

```.ts
type Response = {
  users: {
    id: number
    name: string
    email: string
  }[]
  products: {
    id: number
    name: string
    price: number
  }[]
}

type Users = Response['users']
type User = Users[0]
type Products = Response['products']
type Product = Products[0]

const user: User = { id: 1, name: 'Alice', email: 'alice@example.com' }
const product: Product = { id: 1, name: 'Apple', price: 100 }
```

```
type Users = Response['users']
type User = Users[0]
type Products = Response['products']
type Product = Products[0]
```

これで抽出して定義することができるようになる

書き方は ```[]```配列で書く


## interfaceの関数のオーバーロード

```.ts
interface TmpFunc {
  (a: string): number,
  (a: number): number
}

const tmpFunc: TmpFunc = function (a: number | string) {
  console.log(a);
  return 0
}
```

```(a: number | string)```を```|```でわける



### 型の互換性

https://typescript-jp.gitbook.io/deep-dive/type-system/type-compatibility


## インターセクションのオーバーロード

```.ts
interface FuncA {
  (a: string, b: number): number,
  (a: number, b:string): number
}

interface FuncB {
  (a: string): number
}

let intersectionFunc: FuncA & FuncB;
intersectionFunc = function (a: number | string, b?: string | number) {
  console.log(`${a}: ${b} `)
  return 0
}
```

```
let intersectionFunc: FuncA & FuncB;
```
```&```で繋げる

```

intersectionFunc = function (a: number | string, b?: string | number) {
  console.log(`${a}: ${b} `)
  return 0
}
```
- ```|```で繋げる
- ```b?: string | number```はFuncBで使われていない可能性があるので ```?```を使う必要がある

## レストパラメーターに型を指定する

レストパラメーターとは引数などに何個で入れられるようにする書き方
```...arg```のように書く


### 配列の中身がすべて同じ型

```.ts
function addvancedFn(...arg: number[]):void {
  console.log(arg)
}

addvancedFn(1, 2, 3)
```
```...arg: number[]```ですべて同じ方になる

### 配列の中身が必ず個数が一致しなくてもよい書き方

```.ts
function addvancedFn(...arg: [string, number, boolean?]):void {
  console.log(arg)
}

addvancedFn("1", 2)
```

```...arg: [string, number, boolean?]```

```?```をつけてなくてもいい状態にする

必ず後ろから```?```をつける
前からは付けれない

### 配列の途中まで型をしてあとはレストパラメーターのように使う

```.ts
function addvancedFn(...arg: [string, number, boolean, ...number[]]):void {
  console.log(arg)
}

addvancedFn("1", 2, false, 2 ,3 ,4, 5)

```

```...number[]``` 一番うしろに配列をつける

## 配列やタプルを```readonly```にする

### 配列

readonlyなのでpushなどの変更もできなくなる

```.ts
function addvancedFn(...arg: readonly number[]):void {
  console.log(arg)
  // arg.push("A");
}

addvancedFn(1, 2, 3, 4)
```

```...arg: readonly number[]``` argの後ろに配列の前につける

### タプル

```.ts
function addvancedFn(...arg: readonly [string, number, boolean, ...number[]]):void {
  console.log(arg)
  // arg.push("A");
}

addvancedFn("1", 2, false, 2 ,3 ,4, 5)
```

```...arg: readonly``` argの後ろ 配列の前につける



## 使わないほうがいい

### インデックスシグネチャ オブジェクトに追加できるようにする機能

https://zenn.dev/tsuboi/articles/ee43ddc5a2cd941de138

```.ts
type MovietheatreSeatingAssigment = {
  [seatNUmber: string]: string;
};
```

### 非nullアサーション nullを許容しないを簡潔に書く  ```!```

参考(@k_kind ): 

https://qiita.com/k_kind/items/0e24c54da6fb44403e97

```.ts
const inputElement = document.getElementById('input')!
```