Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Route meta type to a user-extensible empty interfaces #3183

Closed
stevendesu opened this issue Apr 29, 2020 · 4 comments · Fixed by #3385
Closed

Update Route meta type to a user-extensible empty interfaces #3183

stevendesu opened this issue Apr 29, 2020 · 4 comments · Fixed by #3385
Labels
feature request Typescript Typescript related issues

Comments

@stevendesu
Copy link

What problem does this feature solve?

Currently a route's meta field is listed as "any". This makes it impossible for users to define the structure of their own metadata. As an example, I wish to use the "meta" property for middleware. I've created a Middleware type and would like an interface like the following:

declare module "vue-router" {
    interface Route {
        meta: {
            middleware?: Middleware[]
        }
    }
}

If I try to define it this way, I get an error saying that all declarations of "meta" must have identical modifiers, and that "meta" was already defined in router.d.ts... However if I DON'T define it this way then I lose the ability to statically type check this property and catch developers who accidentally pass an incorrect type (e.g. passing a string containing the middleware's name, instead of the middleware object itself)

Now there's admittedly a limitation of this. Currently, it's possible to pass a non-object for the meta of a route. If you want to stick just a number or a string in there, it's completely valid. Making this a user-extensible interface would eliminate that use case (and therefore be a breaking API change) -- but I find it highly suspect that anyone is using the meta field in that way in a production app of any meaningful size. Without key-value pairs, storing only a single string into the "meta" field doesn't feel very useful.

What does the proposed API look like?

export interface Route {
  path: string
  name?: string | null
  hash: string
  query: Dictionary<string | (string | null)[]>
  params: Dictionary<string>
  fullPath: string
  matched: RouteRecord[]
  redirectedFrom?: string
  meta?: {} // <-- replaced "any" with "{}"
}
@posva
Copy link
Member

posva commented Apr 29, 2020

Currently, it's possible to pass a non-object for the meta of a route. If you want to stick just a number or a string in there

It is possible but not supported, only object literals are really supported inside meta. In vue-router-next, its type is Record<string | number, any>, to allow putting anything in there, requiring the user to cast things later on.

I'm open to proposals that are able to keep it the way it is for people not willing to type their meta properties and also to allow different types of meta (should be doable with union types.

You can try out implementing a proposal at https://github.com/vuejs/vue-router-next/blob/master/test-dts/createRouter.test-d.ts. You need to first build the lib with yarn build && yarn build:dts and then run yarn tsd to run the type test

@posva
Copy link
Member

posva commented Apr 30, 2020

BTW I tried using meta?: {} and it won't work. One possible solution is exporting an interface that can be extended:

// in vue-router
export interface RouteMeta
  extends Record<string | number | symbol, any> {}

// in your app
declare module 'vue-router' {
  interface RouteMeta {
    foo?: number
  }
}

@cerinoligutom
Copy link

BTW I tried using meta?: {} and it won't work. One possible solution is exporting an interface that can be extended:

// in vue-router
export interface RouteMeta
  extends Record<string | number | symbol, any> {}

// in your app
declare module 'vue-router' {
  interface RouteMeta {
    foo?: number
  }
}

@posva Tested w/ vue-router@3.1.5 and this works. Would be great if we could have this in the current vue-router but I guess this would break users who are not using key-value pairs for it.

@posva posva added the needs RFC This feature request needs to go through the RFC process to gather more information label Jun 19, 2020
@posva posva removed contribution welcome needs RFC This feature request needs to go through the RFC process to gather more information labels Oct 20, 2020
@kleinfreund
Copy link

kleinfreund commented Sep 23, 2022

For future finders of this issue, to enhance (not override) vue router’s existing type definitions as shown in #3183 (comment), you also have to import vue router in the shims file:

import 'vue-router'

declare module 'vue-router' {
  interface RouteMeta {
    title?: string
  }
}

Otherwise, declaring the module vue-router will override all types that are provided by the original vue-router module.

Also, use a separate shims-vue-router.d.ts file for this enhancement. Adding it to the file that possibly includes shims for vue itself might break some of its enhancements (e.g. silencing TypeScript errors when importing .vue files).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature request Typescript Typescript related issues
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants