Nuxt Composition API でのpropsの使い方や型推論について

# 目次
- [props（プロパティ）とは](#propsプロパティとは)
- [props（プロパティ）の設定](#propsプロパティの設定)
    - [type](#type)
    - [required](#required)
    - [default](#default)
    - [validator](#validator)
- [propsの型推論](#propsの型推論)
    - [PropTypeについて](##PropTypeについて)
 

#props（プロパティ）とは
[vuejs公式 プロパティを用いた子コンポーネントへのデータの受け渡し](https://v3.ja.vuejs.org/guide/component-basics.html#%E3%83%95%E3%82%9A%E3%83%AD%E3%83%8F%E3%82%9A%E3%83%86%E3%82%A3%E3%82%92%E7%94%A8%E3%81%84%E3%81%9F%E5%AD%90%E3%82%B3%E3%83%B3%E3%83%9B%E3%82%9A%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E3%81%B8%E3%81%AE%E3%83%86%E3%82%99%E3%83%BC%E3%82%BF%E3%81%AE%E5%8F%97%E3%81%91%E6%B8%A1%E3%81%97)
ではこう書かれています。

>プロパティはコンポーネントに登録できるカスタム属性です。値がプロパティ属性に渡されると、そのコンポーネントインスタンスのプロパティになります。

要は親コンポーネントから子コンポーネントにデータを渡したい時propsを指定すると値を受け取ることができるということです。
その値を受け取る際にいろんな設定を行うことができます。

#props（プロパティ）の設定
- type
- required
- default
- validation

##type
プロパティの型のことです。

`[String,Number,Boolean,Array,Object,Function,Promise]`
などの型を指定することができます。

詳しくは[vue.js公式 型の検査](https://v3.ja.vuejs.org/guide/component-props.html#%E5%9E%8B%E3%81%AE%E6%A4%9C%E6%9F%BB)をご覧ください。

```js:childComponent.vue
props:{
  type: {
    type: String,
  },
}
```

`text`はString (文字列)で指定していますが、
0のような`number型`の値を子コンポーネントに渡すと下記のようなエラーが発生します。

```console
[Vue warn]: Invalid prop: type check failed for prop "text". Expected String with value "0", got Number with value 0.
```

##required
reuqiredは必須のプロパティか、を指定する設定になります。
`true`,`false`で指定します。

```js:childComponent.vue
props:{
  text: {
    type: String,
    required: true,
  },
}
```

`text`は必須のプロパティですが、親コンポーネントから渡されなかった場合下記のようなエラーが発生します。

```console
[Vue warn]: Missing required prop: "text"
```

##default
`default`は主に`required`を設定していない際に利用します。

```js:childComponent.vue
props:{
  text: {
    type: String,
    required: false,
    default: ''
  },
}
```

必須ではないので親コンポーネントから何も渡されないことがあります。
なのでデフォルトを指定すると親コンポーネントから何も渡されなかった場合デフォルトの値がpropsの値になります。

オブジェクトや配列については`factory関数`で指定してあげる必要があるのですが、下記のqiitaの記事がわかりやすかったので紹介させていただきます
[[Vue.js] なぜpropsのdefault値にObjectやArrayを指定する際にfactory関数にする必要があるのか](https://qiita.com/hogesuke_1/items/c74463de1a1eee802ca8)

##validator

`validator`はpropsの値をもっと細かくバリデーションをかけたい場合に使います。

```js:childComponent.vue
props:{
  length: {
    type: String,
    required: true,
    validator: (value: string) => {
        return ['long', 'regular', 'short'].includes(value)
    },
  },
}
```

`validator`は返り値がfalseであった際、コンソールエラーが起きます。

```
[Vue warn]: Invalid prop: custom validator check failed for prop "length".
```

上記のコードは
引数のvalue(親コンポーネントで指定している値)が`'long', 'regular', 'short'`のどれかに一致しているか。を確認している内容になります。

なのでpropsの`length`は`string型`であり`long`または`regular`または`short`でない時にコンソールエラーが起きます。



#propsの型推論
次はpropsの型推論についてです。

>setup() 関数においては、props 引数に型をつける必要はありません。setup() 関数は　props コンポーネントオプションから型を推論するからです。


引用元
[Vue.js コンポジション API とともに使用する](https://v3.ja.vuejs.org/guide/typescript-support.html#%E3%82%B3%E3%83%B3%E3%83%9B%E3%82%9A%E3%82%B7%E3%82%99%E3%82%B7%E3%83%A7%E3%83%B3-api-%E3%81%A8%E3%81%A8%E3%82%82%E3%81%AB%E4%BD%BF%E7%94%A8%E3%81%99%E3%82%8B)


```js:childComponent.vue
import { defineComponent } from '@nuxtjs/composition-api'

export default defineComponent({
  props: {
    text: {
      type: String,
      required: true;
    },
    text2: {
      type: String,
      required: false,
      default: null,
    },
    num: {
      type: Number,
      required: true;
    },
    array: {
      type: Array,
      required: true;
    },
    obj: {
      type: Object,
      required: true;
    },   
  },
  setup(props) {
  },
})

// propsの型

// props: {
//    text: string
//    text2?: string | undefined
//    num: Number
//    array: unknown[]
//    obj: Record<string, any>
//
```

実際にpropsの型を調べた際、ちゃんと型推論がされていました。
requiredの値によってオプション（必須でない）が付くみたいですね。

しかし、配列やオブジェクトに関しては`unknown[]`や`Record<string, any>`と細部までは定義されていませんでした。
こちらの問題を解決するためにはvueで用意されているPropTypeを利用する必要があります。

##PropTypeについて


```ts:childComponent.vue
import { defineComponent, PropType } from '@nuxtjs/composition-api'

interface ArrayItem {
  name: string
}

interface Obj {
  title: string
}

export default defineComponent({
  props: {
    text: {
      type: String,
      required: true;
    },
    text2: {
      type: String,
      required: false,
      default: null,
    },
    num: {
      type: Number,
      required: true;
    },
    array: {
      type: Array as PropType<ArrayItem[]>,
      required: true;
    },
    obj: {
      type: Object as PropType<Obj>,
      required: true;
    },   
  },
  setup(props) {
  },
})

// propsの型

// props: {
//    text: string
//    text2?: string | undefined
//    num: Number
//    array: ArrayItem[]
//    obj: Obj
//
```

上記のコードのように型アサーションでPropTypeを指定すると
unknownやanyではない指定した型が割り当てられていました。

しかし`PropType`はバリデーションをかけているのではなく、型を上書きしているので
validatorやdefaultを設定する必要があります。

詳しくはこちらを下記の公式のページをご覧ください。
[Props にアノテーションをつける](https://v3.ja.vuejs.org/guide/typescript-support.html#%E6%88%BB%E3%82%8A%E5%80%A4%E3%81%AE%E5%9E%8B%E3%81%AB%E3%82%A2%E3%83%8E%E3%83%86%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E3%81%A4%E3%81%91%E3%82%8B)



# 参照
https://v3.ja.vuejs.org/guide/typescript-support.html

https://qiita.com/hogesuke_1/items/c74463de1a1eee802ca8