Nuxt.jsのComposition APIで学ぶ、TypeScriptのUtility Typesについて

## Utility Typesとは
TypeScriptが提供している特徴的な型です。
型の定義をより簡潔に書けるため、めちゃくちゃ便利です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/29b72122-9a02-a597-bd48-864c71b94473.png)


## Partial`<Type>`
すべてのプロパティを**任意のプロパティ**にしてくれます。

```vue:sample.vue
<script lang="ts">
import { defineComponent, ref, computed } from '@nuxtjs/composition-api';

interface Person {
  firstName: string;
  lastName: string;
  age: number;
}

export default defineComponent({
  setup() {
    const taro = ref<Partial<Person>>({
      firstName: 'Taro',
      lastName: 'Tanaka',
    });

    console.log(taro.value); // { firstName: "Taro", lastName: "Tanaka" }
  },
});
</script>
```

## Required`<Type>`
すべてのプロパティを**必須のプロパティ**にしてくれます。
`firstName`, `lastName`, `age`のどれかが欠けた瞬間エラーとなります。

```vue:sample.vue
<script lang="ts">
import { defineComponent, ref, computed } from '@nuxtjs/composition-api';

interface Person {
  firstName: string;
  lastName: string;
  age: number;
}

export default defineComponent({
  setup() {
    const jiro = ref<Required<Person>>({
      firstName: 'Jiro',
      lastName: 'Tanaka',
      age: 35,
    });

    console.log(jiro.value); // { firstName: "Jiro", lastName: "Tanaka", age: 35 }
  },
});
</script>
```

## Readonly`<Type>`
すべてのプロパティを**読み取り専門のプロパティ**にしてくれます。
`Saburo.lastName = "Sato"`のように値を上書きしようとするとエラーが発生します。

```vue:sample.vue
<script lang="ts">
import { defineComponent, ref, computed } from '@nuxtjs/composition-api';

interface Person {
  firstName: string;
  lastName: string;
  age: number;
}

export default defineComponent({
  setup() {
    const Saburo = ref<Readonly<Person>>({
      firstName: 'Saburo',
      lastName: 'Tanaka',
      age: 30,
    });

    // Property 'lastName' does not exist on type
    Saburo.lastName = "Sato"; // エラーが発生

    console.log(Saburo.value); // { firstName: "Saburo", lastName: "Tanaka", age: 30 }
  },
});
</script>
```

## Record`<Keys, Type>`
`Record`は2つの引数を取ります。
`<Keys>`はプロパティ、`<Type>`型を持つレコードを作ります。

以下の例の場合、
`<Keys>`は**`Prefectures`**が指定されており、`<Type>`が**`Person`**となっています。

```vue:sample.vue
<script lang="ts">
import { defineComponent, ref, computed } from '@nuxtjs/composition-api';

interface Person {
  firstName: string;
  lastName: string;
  age: number;
}

type Prefectures = "Tokyo" | "Osaka" | "Aiti";

export default defineComponent({
  setup() {
    const family = ref<Record<Prefectures, Person>>({
      Tokyo: {
        firstName: "Taro",
        lastName: "Tanaka",
        age: 40
      },
      Osaka: {
        firstName: "Jiro",
        lastName: "Tanaka",
        age: 35
      },
      Aiti: {
        firstName: "Saburo",
        lastName: "Tanaka",
        age: 30
      },
    })
  },
});
</script>
```

## Exclude`<Type, Union>`
Exclude は`<Type>`型のプロパティから`<Union>`型の**共通部分を削除した**型を構築します。
以下の例の場合、
**TypeA**は、`id | name | age`で、**TypeB**は、`id | address | age`となっています。
**TypeA**と**TypeB**の共通部分は、`id | age`です。
なので**TypeA**の残った**`name`**が型となります。

```vue:sample.vue
<script lang="ts">
import { defineComponent, ref, computed } from '@nuxtjs/composition-api';

interface TypeA {
  id: number;
  name: string;
  age: undefined;
}
interface TypeB {
  id: number;
  address: string;
  age: undefined;
}

export default defineComponent({
  setup() {
    const excludeType = ref<Exclude<keyof TypeA, keyof TypeB>>(); // name
  },
});
</script>
```

## Extract`<Type, Union>`
Extract は`<Type>`型のプロパティから`<Union>`型の**共通部分だけを抽出した**型を構築します。
以下の例の場合、
**TypeA**は、`id | name | age`で、**TypeB**は、`id | address | age`となっています。
**TypeA**と**TypeB**の共通部分である、**`id | age`**が型となります。

```vue:sample.vue
<script lang="ts">
import { defineComponent, ref, computed } from '@nuxtjs/composition-api';

interface TypeA {
  id: number;
  name: string;
  age: undefined;
}
interface TypeB {
  id: number;
  address: string;
  age: undefined;
}

export default defineComponent({
  setup() {
    const excludeType = ref<Extract<keyof TypeA, keyof TypeB>>(); //id, age
  },
});
</script>
```

## Pick`<Type, Keys>`
Pickは既に存在する`<Type>`型の中から`<Keys>`で選択したプロパティのみを含んだ新たな型を構築します。
以下の例の場合、`Person`から`firstName | lastName`の2つを選択して新たな型を作っています。

```vue:sample.vue
<script lang="ts">
import { defineComponent, ref, computed } from '@nuxtjs/composition-api';

interface Person {
  firstName: string;
  lastName: string;
  age: number;
}

export default defineComponent({
  setup() {
    const siro = ref<Pick<Person, 'firstName' | 'lastName'>>({
      firstName: 'Siro',
      lastName: 'Tanaka',
    });
  },
});
</script>
```

## Omit`<Type, Keys>`
Omitは既に存在する`<Type>`型の中から`<Keys>`で選択したプロパティのみを削除した新たな型を構築します。
以下の例の場合、`Person`から`lastName`を削除して新たな型を作っています。

```vue:sample.vue
<script lang="ts">
import { defineComponent, ref, computed } from '@nuxtjs/composition-api';

interface Person {
  firstName: string;
  lastName: string;
  age: number;
}

export default defineComponent({
  setup() {
    const goro = ref<Omit<Person, 'lastName'>>({
      firstName: 'Goro',
      age: 20,
    });
  },
});
</script>
```

## Parameters`<Type>`
Parametersは、関数型の`<Type>`の引数の型をタプル型（複数の値を保持することのできる型）として抽出した型を構築します。

```vue:sample.vue
<script lang="ts">
import { defineComponent, ref, computed } from '@nuxtjs/composition-api';

export default defineComponent({
  setup() {
    const debugProfile = (name: string, age: number): void => {
      console.log({ name, age });
    };
    const rokuro = ref<Parameters<typeof debugProfile>>(['rokuro', 15]);

    debugProfile(...rokuro.value);
  },
});
</script>
```

## ConstructorParameters`<Type>`
ConstructorParametersは、コンストラクタの`<Type>`の引数の型をタプル型（複数の値を保持することのできる型）として抽出した型を構築します。

```vue:sample.vue
<script lang="ts">
import { defineComponent, ref, computed } from '@nuxtjs/composition-api';

export default defineComponent({
  setup() {
    class Profile {
      constructor(public name: string, public age: number) {}
    }

    const nanaro = ref<ConstructorParameters<typeof Profile>>(['nanaro', 10]);
    console.log(nanaro.value);
  },
});
</script>
```