'Vue 3 - Extend a Vue component with additional props with Typescript support
I'm trying to extend a 3rd party Vue component (from PrimeVue) and add additional props, but I'd like to keep the typings of the original component and also union them with my additional props.
For example, using the 3rd party Button component below which has typings that enforce "label" must be a string, my IDE (VSCode + Vetur) will mark it as an error if I attempt to pass a numeric.
If I were to extend the Button class into my own component, BaseButton, using the common pattern below, I will be able to pass a numeric or any other type to the label prop without the IDE complaining. Besides maintaining the typings of the original Button component, I want to union it with my additional props, e.g. myProp: string
below.
<template>
<Button v-bind="$attrs" :label="myComputedLabel">
<template v-for="(_, slot) of $slots" v-slot:[slot]="scope">
<slot :name="slot" v-bind="scope"/>
</template>
</Button>
</template>
<script>
export default defineComponent({
name: 'BaseButton',
props: {
myProp: {
type: String
}
},
setup(props) {
const myComputedLabel = computed(() => {
return `Hello ${props.myProp}`;
});
}
})
</script>
In my case, the 3rd party component is written in Vue with vanilla JS but has an external Button.d.ts typings file. I'm not sure if that matters.
Solution 1:[1]
you can read it from the component's setup
option, the props
is the first argument of setup
type MyButtonProp = Parameters<NonNullable<typeof Button.setup>>[0]
Simply, here's a Generic Type
type ComponentProps<T extends { setup?: any }> = Parameters<NonNullable<T['setup']>>[0]
usage
type MyButtonProp = ComponentProps<typeof Button>
Solution 2:[2]
If you're using Vue 3 + script setup
, you can do it like this
import Button from 'primevue/button';
type ButtonInstance = InstanceType<typeof Button>
defineProps<{
label: ButtonInstance['$props']['label'] | number
}>()
For setup function
import { PropType } from 'vue'
defineComponent({
props: {
label: {
type: [String, Number] as PropType<ButtonInstance['$props']['label'] | number>,
}
},
})
Solution 3:[3]
You could use Typescript + Setup
<script setup lang="ts">
import { computed } from "vue"
let props = defineProps<{ myProp: string | number }>()
const myComputedLabel = computed(() => {
return `Hello ${props.myProp}`;
})
</script>
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | wobsoriano |
Solution 3 | bill.gates |