Nico Prat
Typing Vue Router pages props
One step closer to full routing type safety in Vue
We are progressively adding TypeScript in our Vue application since a couple years ago, and recently migrating to Vue 3 greatly improved our developer experience. There are still some holes left though, notably in the routing part, and there is not always an easy way to fix them.
Let's say you have a simple route that loads a page component, which needs a category
prop:
<script lang="ts" setup>
// pages/Category.vue
defineProps({
category: {
type: String,
required: true,
}
});
</script>
// routes.ts
const routes = [
{
path: '/blog/:category,
name: 'Category',
component: () => import('@/pages/Category.vue'),
// no type safety here
props: ({ params }) => ({ category: params.category })
}
]
By default, TypeScript can't help us make sure we pass the correct props to the component.
Exporting props to force type safety#
The simplest solution is to expose the props
definition as a simple object, so we can check against them:
<script lang="ts" setup>
// pages/Category.vue
defineProps(props);
</script>
<script lang="ts">
// Needs a separate script tag without `setup` sugar
const props = {
category: {
type: String,
required: true,
}
};
// Exporting a type on its own allows us to prevent loading the whole component in the routes definition
export type Props = typeof props
</script>
// routes.ts
import type { Props } from '@/pages/Category.vue'
const routes = [
{
path: '/blog/:category',
name: 'Category',
component: () => import('@/pages/Category.vue'),
// type safety is now ensured
props: ({ params }) => ({ mode: params.mode } as Props)
}
]
This works pretty well for the easiest cases, but we usually have stricter types that use Vue PropTypes
utility, like this:
const props = {
category: {
type: String as PropType<'development' | 'design'>,
required: true,
}
};
ExtractPropTypes to the rescue#
Fortunately, Vue exposes a TypeScript utility that can extract the prop types to narrow our props type:
// routes.ts
import type { ExtractPropTypes } from 'vue';
import type { Props } from '@/pages/Category.vue'
const routes = [
{
path: '/blog/:category',
name: 'Category',
component: () => import('@/pages/Category.vue'),
// type safety is now ensured
props: ({ params }) => ({ category: params.category } as ExtractPropTypes<Props>)
}
]
Always keep in mind that this does not check the real value at runtime, so a prop validator might still be necessary.
In action#
You can have a look at this example in the Vue SFC Playground:
In the future#
We might be able to extract prop types even more easily if this pull request proposing more TypeScript utilities is merged. For instance, with ExtractComponentProp
we could remove the annoying step of exporting the props as a plain object completely:
const options = {
props: { foo: String },
setup(){
// ...
}
}
const Comp = defineComponent(options)
expectType<ExtractComponentProp<typeof Comp>>(options.props)
Let's see what cool features the future brings to keep our code even safer!