なんかいろいろと書いてくブログ

関東のどこかで働く、一般人

【Nuxt】propsの型の曖昧さについて

初めに

本業で、携わっているサービスはフロントをNuxt × TypeScriptで書いていたのですが、
直近でReactに乗せ換える話が出てきました

乗せ換え作業はおそらく私がほぼ一人で進めることになると思っているのですが、
その際に、なぜReact化する化をCTO含めチーム全体に説明する必要があるので

その理由の一つであるPropsの型定義の曖昧差について書いていきます

propsの型検査について

参照

プロパティを用いた子コンポーネントへのデータの受け渡し

型検査

型検査について

propsはいくつかの、組込みで型指定をすることができます

  props: {
    hoge: String
  }

また、ユーザー定義についてはPropTypeで型推論することができます

 props: {
    hoge: {
      type: Object as Vue.PropType<THoge>
    }
  }

上記の設定により、propsを受け取るComponent内では型を持ったpropsを使用することができます hoge:string

hoge;object

また、親Componentから値を渡す際に、異なる型の値を渡すとエラーが確認できます

error

propsの型の曖昧さについて

上記のように型を定義することができればpropsを利用するComponent内では
TypeScriptの型のアシストを受けて実装するとができます

しかし、propsに対する型定義は厳密とは言えず
割となんでもやることができます、
なぜなら、親Componentでは子Componentでの定義を知らないので v-bindまたは、v-modelにはどのような型のプロパティでも渡すことができてしまいます

例えば、下記の例では子ComponentではTHogeというObjectが渡されることを期待していますが、
親Componentではstring型を渡しています この場合、Consoleにエラーが出力するものの、コンパイルエラーではないので騙せてしまいます

子Component

import Vue from 'vue'

type THoge = {
  hoge: string,
  hogehoge: number
}

export default Vue.extend({
  name: 'NuxtTutorial',
  props: {
    hoge: Object as Vue.PropType<THoge>
  },
  methods: {
    getHoge(): THoge {
      return this.hoge
    }
  }
})
</script>

親Component

export default Vue.extend({
  name: 'IndexPage',
  components: {
    Tutorial
  },
  data(){
    return {
      hoge: 
        hoge: "hoge"
    }
  }
})

Vue DevToolでporpsの中身を確認してもstringで渡せていることが確認できます

vue devtool

これを回避するために、普段は子Componentで定義したtypeを親で読み込んで定義することで propsとして意図しない型を渡すことを防いでいます

子Component

import Vue from 'vue'

// typeもexport
export type THoge = {
  hoge: string,
  hogehoge: number
}

export default Vue.extend({
  name: 'NuxtTutorial',
  props: {
    hoge: Object as Vue.PropType<THoge>
  },
  methods: {
    getHoge(): THoge {
      return this.hoge
    }
  }
})
</script>

親Component

import Vue from 'vue'

// Vueとともにtypeもimport
import Tutorial, { THoge } from "~/components/Tutorial.vue"

type DataType = {
  hoge: THoge
}

export default Vue.extend({
  name: 'IndexPage',
  components: {
    Tutorial
  },
  data(): DataType {
    return {
      hoge: {
        hoge: "hoge",
        hogehoge: 1
      }
    }
  }
})

とはいえ、この方法は親Componentでも同じ型定義をしているだけであって
やはり、propsに対する型定義をは言えないのが現状です

最後に

propsへの型定義が曖昧であることはVueをやめてReactに理由の一つにすぎません
Vueがつらいと感じるのは大体TypeScriptとの相性の悪さに起因することが多く、TypeScriptとの相性の悪さをとても実感しました  

とはいえ、現在サービスがNuxtで構築したことについては後悔はなく、
特にtemplateベースでかけるのは非常に書きやすかったです
(おそらく、Reactだったらリリースに間に合っていなかった)