With Vue Router version 4, which is currently in beta.11 in vue-router-next repo, there is a documentation page about how to define meta fields custom type interface with TypeScript.
declare module 'vue-router' {
interface RouteMeta {
// is optional
isAdmin?: boolean
// must be declared by every route
requiresAuth: boolean
}
}
To be placed along the Vue shim module declaration. Mine is looking like:
declare module '*.vue' {
import { defineComponent } from 'vue';
const component: ReturnType<typeof defineComponent>;
export default component;
}
declare module 'vue-router' {
interface RouteMeta {
isPublic?: boolean;
}
}
However, this is not working. Instead this way of defining the interface seems to overwrite the interface that is shipped with the package, or rather declaring 'vue-router' module seems to do that.
What would be the correct way of defining custom meta field types?
Vue 2 already has good support for TypeScript, and the recently published Vue 2.7 backported a lot of useful features from Vue 3, like composition API, <script setup> , and defineComponent , further improving the developer experience of TypeScript in Vue.
Sometimes, you might want to attach arbitrary information to routes like transition names, who can access the route, etc. This can be achieved through the meta property which accepts an object of properties and can be accessed on the route location and navigation guards.
Their documentation is wrong or at best incomplete.
A Module Augmentation uses the same syntax as an Ambient Module declaration and is only considered an augmentation when it is within a module file itself. A module is defined, as per the ECMAScript specification, as a file containing one or more top level import
or export
statements.
The snippet in a file that is not a module does exactly what you've noticed. It supplants any other types for the 'vue-router'
package instead of augmenting them. But we want to augment that package's types, not replace them.
However, a declare module
statement that is intended as a declaration, not an augmentation, must be in a file that is, conversely, not a module. That is, in a file not containing any top level import
or export
statements.
To resolve this, move the declare module 'vue-router' {...}
to a separate file (say, augmentations.d.ts
), and make that file a module by beginning it with export {}
.
// augmenations.d.ts
// Ensure this file is parsed as a module regardless of dependencies.
export {}
declare module 'vue-router' {
interface RouteMeta {
// is optional
isAdmin?: boolean
// must be declared by every route
requiresAuth: boolean
}
}
Now let's come back and take look at the original code in question.
// shims-vue.d.ts
declare module '*.vue' {
import { defineComponent } from 'vue';
const component: ReturnType<typeof defineComponent>;
export default component;
}
declare module 'vue-router' {
interface RouteMeta {
isPublic?: boolean;
}
}
The two declare module
statements cannot exist in the same file because one of them is trying to declare a module, '*.vue'
, and the other to augment one. Therefore, we will leave the declare module '*.vue' {...}
where it is, as it is functioning as intended.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With