propsの型をジェネリックに定義したい
selectフィールドのコンポーネントがあり、選択肢一覧をArrayやObjectで渡したいときってありますよね。
selectのコンポーネントが色々なところで利用されるとすると、中身の型は渡すまで決まらないことがあるので、選択肢一覧のpropsの値をジェネリックな型で定義したいと思い調べた。
ここらへんを見ても特に書いていない。
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のほうが書きやすいと思ったんだけど、もしかしたら自分が知らないだけかもしれない。知っている人がいたら教えてほしい。