[Vue] Vue 3.3 Generic Component
In Vue 3.3, you can create generic components.
Generic is an important feature in many statically typed languages, including TypeScript. It’s basically a way for you to create a function or a class with placeholder types, called generic type parameters.
function countItems<T>(list: T[]) {
return list.length
}
So T
is a placeholder type, that means it can be anything. The list
parameter can be an array of strings, it can also be an array of numbers, it all depends on how this function is used.
const count = countItems([1,2,3])
In this example, since we’re passing in an array of numbers, list
will be an array of numbers, and T
will be the number
type.
The type requirements for using a generic function are more flexible, hence the function is more reusable.
Now you can apply this generic concept to your Vue.js components. The generic attribute is where you declare your generic type parameters.
<script setup lang="ts" generic="T">
defineProps<{ list: T[] }>()
</script>
<template>
<p>{{ list.length }}</p>
</template>
So if you imagine a component as a TypeScript function, the generic
attribute would correspond to the angular brackets:
export defualt function<T>(
list: T[]
) {
<template>
<p>{{ list.length }}</p>
</template>
}
Just like a generic function, a generic component is more flexible in terms of type requirements, allowing your component to work with props of different data types. In this example, we want the list
prop to be an array, but we don’t want to specify the type of the items that this array should contains. That’s why we use T
as a placeholder type.
When we’re using this component, we can pass it an array of numbers, or an array of strings, the component would still work.
<MyComponent :list="[1,2,3]">
T
refers to the type of each individual item within the array, it can be any type. With the way that we’re using the component here, T
will be number
.
But, the way you use T
within the component has to be consistent. For example, if we need a second list
as another prop:
<script setup lang="ts" generic="T">
defineProps<{
list: T[],
list2: T[]
}>()
</script>
<template>
<p>{{ list.length }}</p>
</template>
T
would need to be the same type for both list
and list2
.
So passing in an array of a different type would not work:
<MyComponent :list="[1,2,3]" :list2="['one', 'two', 'three']">
list2
has to be the same as list
since they’re both typed as T[]
. Since list
is an array of numbers, list2
has to be an array of numbers too.
So if you need list2
to be a different type of list, you have to define another placeholder type:
<script setup lang="ts" generic="T, U">
defineProps<{
list: T[],
list2: U[]
}>()
</script>
And by the way, they don’t have to be named as T
or U
, this is just a naming convention for generics.
If you want to learn more about generic component, you can check out the Vue.js documentation for that. And if you want to learn more about TypeScript generics, you can check out the TypeScript documentation for that.
Code
📁 /src/components/MyComponent.vue
<script setup lang="ts" generic="T, U">
defineProps<{
list: T[],
list2: U[]
}>()
</script>
<template>
<p>{{ list.length }}</p>
</template>
📁 /src/App.vue
<script setup lang="ts">
import { ref } from 'vue'
import MyComponent from './components/MyComponent.vue'
const foo = ref('foo')
</script>
<template>
<MyComponent :list="[1,2,3]" :list2="['one', 'two', 'three']">
</template>