技術とかの雑なToday I Learnedメモ

propsの型をジェネリックに定義したい

propsの型をジェネリックに定義したい

selectフィールドのコンポーネントがあり、選択肢一覧をArrayやObjectで渡したいときってありますよね。

selectのコンポーネントが色々なところで利用されるとすると、中身の型は渡すまで決まらないことがあるので、選択肢一覧のpropsの値をジェネリックな型で定義したいと思い調べた。

プロパティ — Vue.js

TypeScript のサポート — Vue.js

ここらへんを見ても特に書いていない。

Generically Typed Vue.js Components - Abdelrahman's Blog

ググったら上記の記事が出てきた。

import { defineComponent, PropType } from 'vue';
function defineGenericComponent<T = unknown>() {
  return defineComponent({
    props: {
      options: {
        type: Array as PropType<T[]>,
        required: true,
      },
      value: {
        type: null as unknown as PropType<T | undefined>,
        default: undefined as unknown,
      },
    },
    emits: {
      change: (payload: T) => true,
    },
  });
}

↑は記事内のコードの引用

マジ?

defineComponentをラップしてジェネリックなコンポーネントを作り、そこでpropsに呼び出し側から型を渡している。

うーむ、これだとジェネリックにしたいコンポーネント全てでラップしないといけないし、記述量もファイル数も増える……。

Reactだと、

type Props<T> = { list: T[], selectedItem?: T }
const Select<T> = (props: Props<T>) => {
  // コンポーネントの中身
}

こんな感じでジェネリックにできる。

Vueだとこういうことができないのはちょっと困ったりするが、他の困った人たちはどうやって解決しているのだろうか……?

selectのコンポーネント内で、渡される可能性のある型をUnion Typesで定義すれば可能だけど、そうなると使用箇所が増えるごとに型定義をimportしてUnion Typesに追記する必要ができてしまう。

export default defineComponent({
  props: {
    list: {
      type: Array as PropType<A[] | B[] | C[]>, // 使われる箇所が増えるたびに足す
      required: true
    },
    selectedItem: {
      type: [A, B, C], // 使われる箇所が増えるたびに足す
      required: false,
      default: null
    }
  }
})

ここはReactのほうが書きやすいと思ったんだけど、もしかしたら自分が知らないだけかもしれない。知っている人がいたら教えてほしい。