Search code examples
vuejs3typescript-generics

How to enforce type on Vue generic component for multiple properties


When defining a generic Vue component, it appears that the resulting type of the generic is the union of all arguments. Is it possible to limit this so the type of all properties must match?

For example, a generic select component:

<script setup lang="ts" generic="T extends string">
  // We want to enforce modelValue & options come from the same set of possible values
  defineProps<{
    modelValue?: T
    options: readonly T[]
  }>()

const emit = defineEmits<(e: 'update:modelValue', val?: T) => void>()
// Have a <select with options
</script>

I would hope that with the above component, I would get an error with the below usage as the two types being passed do not intersect

<script setup lang="ts">
type ValidOptions = "Big"|"Small"|"Medium"
const value = ref<ValidOptions>("Small")
</script>
<template>
  <!-- options & modelValue have different types here -->
  <MySelect :options="['Chicken','Little'] as const" v-model="value" />
</template

However, when defining it in this way the generic type ends up being the union of all types: "Chicken"|"Little"|"Big"|"Small"|"Medium", and so no error is reported


Solution

  • You can type your select like this:

    See on Vue SFC Playground

    <component :is="MySelect<ValidOptions>" :options="['Chicken','Little'] as const" v-model="value" />