Search code examples
typescriptvue.jsvue-composition-apivue-propsvue-options-api

Vue3 shared props between components


I've recently switched from Vue2 to Vue3 but I'm sort of confused to what the best practice is to shared props between multiple components. I want to make input components which share the common props like "type", "name", etc. Previously I did it with mixins but I wonder if this still is the best way to do so.

Let me give an exampleof what I tried via the options-api:

// Home.vue
<template>
    <p>Home</p>
    <p>{{ type }}</p>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
import { sharedInputProps } from '../admin/composables/sharedInputProps'

export default defineComponent({
    props: {
        ...sharedInputProps,
    },
    setup() {
        return {
        }
    },
})
</script>
// sharedInputProps.ts
import { ComponentPropsOptions } from 'vue'

export const sharedInputProps: ComponentPropsOptions = {
    type: {
        type: String,
        required: true,
        default: 'text',
    }
}

This works but I get no typehinting/autocompletion in my IDE to what the props are in Home.vue (in this case <p>{{ type }}</p>.

This is how I would do it with mixins:

// Home.vue
<template>
    <p>Home</p>
    <p>{{ type }}</p>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
import InputProps from '../admin/mixins/InputProps.vue'


export default defineComponent({
    mixins:[
        InputProps,
    ],
    setup() {
        return {
        }
    },
})
</script>
// InputProps.vue
<script lang="ts">
import { defineComponent } from 'vue'

export default defineComponent({
    props: {
        type: {
            type: String,
            required: true,
            default: 'text',
        }
    },
})
</script>

This works as intended and it gives me proper typehinting/autocompletion.

But I wonder how this should be done with the composition-api using the defineProps method. I got it to work directly defining the props in Home.vue with typehinting/autocompletion, as shown below:

// Home.vue
<template>
    <p>Home</p>
    <p>{{ type }}</p>
</template>

<script setup lang="ts">
import { defineProps, withDefaults } from 'vue'
interface Props {
    type?: string
}

const props = withDefaults(defineProps<Props>(), {
    type: 'text',
})

</script>

But as the documentation (https://vuejs.org/guide/typescript/composition-api.html#typing-component-props) states, it cannot be loaded from an external file.

So my question is, since using an external file with the composition-api is not supported (yet?) and an external file using the options-api and using the spread operator to destructurize an object is not giving typehinting/autocompletion, is mixins still the best practice to share props? Or am I misconfiguring something in my sharedProps.ts file (or somewhere else)?

I'm running the latest version of Vue 3(3.2.47) and I'm using the latest version of PHPStorm(2023.1) as my IDE.


Solution

  • ComponentPropsOptions type prevents sharedInputProps to be implicitly typed.

    If sharedInputProps are supposed to be compatible with ComponentPropsOptions type, it's possible do use satisfies in TypeScript 4.9 or higher:

    export const sharedInputProps = {
        type: {
            type: String,
            required: true,
            default: 'text',
        }
    } satisfies ComponentPropsOptions
    

    This keeps the correct type when it's used as props: { ...sharedInputProps } and allows to spot an error in place where it's defined if it's incorrect.