11

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?

ux.engineer
  • 10,082
  • 13
  • 67
  • 112
  • 1
    Because their documentation is wrong or incomplete. That snippet, as documented does exactly what _you_ say. It supplants the declarations for the `'vue-router'` package with your own instead of augmenting them. To fix, move `declare module 'vue-router'` to a separate file, and make that file a module by beginning it with `export {}`. Those two `declare module` blocks cannot exist in the same file because one of them is trying to declare a module and the other to augment one. – Aluan Haddad Sep 21 '20 at 20:14
  • Thanks @AluanHaddad, this seems to have solved the problem. Moved these declarations to separate files. But how does adding `export {};` statement before `declare module 'vue-router'` block change it from overwriting the package's module declaration to augmenting it instead? – ux.engineer Sep 21 '20 at 20:21
  • 1
    I added some documentation links in the answer. Basically the difference in meaning is that `import`/`export` change the meaning of a file from script to module. And the syntax for declaring and augmenting is the same but means one thing in one context and another in the other. – Aluan Haddad Sep 21 '20 at 20:29

2 Answers2

23

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.

Aluan Haddad
  • 29,886
  • 8
  • 72
  • 84
0

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

  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Mar 25 '23 at 21:28