ux.engineer
ux.engineer

Reputation: 11358

How to declare TypeScript type interface for custom meta fields in Vue Router v4?

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?

Upvotes: 13

Views: 8857

Answers (2)

Aluan Haddad
Aluan Haddad

Reputation: 31853

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 type declarations 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.

Upvotes: 26

secretLi
secretLi

Reputation: 1

I put the file router.ts into a types dir,which is on the same level with src,then i modify the tsconfig.json "include": ["src//*.ts", "src//.d.ts", "src/**/.tsx", "src//*.vue","types//*.ts"],and it works

Upvotes: 0

Related Questions