Search code examples
typescripttypesmoduledeclarationtype-inference

TS4023: Exported Variable <x> has or is using name <y> from external module but cannot be named


I've seen this answered before, but they don't seem to cover this specific use case (or they don't work/help)

import {Route} from 'vue-router';


export const detailRoute = {
  path: '/detail/:id',
  component: Detail,
  props: (route: Route) => ({
    state: route.query.state
  })
};

detailRoute uses Route, which I am importing, but I guess as a named import {Route} it doesn't work? Is there a different/better way to do this that will work? I tried export {Route}; as well, but that didn't help.

tsconfig.json:

    {
      "compilerOptions": {
        "target": "ES2017",
        "module": "ES2015",
        "moduleResolution": "Node",
        "sourceMap": true,
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "forceConsistentCasingInFileNames": true,
        "allowSyntheticDefaultImports": true,
        "noEmitHelpers": true,
        "importHelpers": true,
        "pretty": true,
        "alwaysStrict": true,
        "declaration": true,
        "declarationDir": "./types",
        "lib": [
          "DOM",
          "ES2017",
          "DOM.Iterable",
          "ScriptHost"
        ],
        "baseUrl": "./client",
        "paths": {
          "styles/*": ["./app/core/styles/*"],
          "core/*": ["./app/core/*"],
          "components/*": ["./app/components/*"],
          "containers/*": ["./app/containers/*"],
          "assets/*": ["./assets/*"],
          "config/*": ["./config/*"]
        }
      }
    }

Exact error:

TS4023: Exported variable 'detailRoute' has or is using name 'Route' from external module "/Users/chris/<projectname>/node_modules/vue-router/types/router" but cannot be named.


Solution

  • The compiler is failing to figure out the exact shape of detailRoute, because it does not know the shape of Route.

    Option 1

    One way around this is to import Route from its source, thereby providing the information that the compiler needs to determine the shape of detailRoute.

    import { Route } from "./../node_modules/vue-router/types/router";
    
    export const detailRoute = {
      props: (route: Route) => null,
    };
    

    Since the index.d.ts file in vue-router (which you were importing in the question) re-exports Route, it does not provide the direct reference to Route that the compiler needed.

    Option 2

    Another option is to opt detailRoute out of static typing altogether.

    import { Route } from 'vue-router'; // index.d.ts
    
    export const detailRoute: any = {
      props: (route: Route) => null,
    };
    

    Since any opts-out of static typing, the compiler does not need to figure out the shape of detailRoute.

    Option 3

    A further is option is what you did in your own answer. Since you provided the type annotation, the compiler again does not need to figure out the shape of detailRoute.

    import { Route, RouteConfig } from 'vue-router'; // index.d.ts
    
    export const detailRoute: RouteConfig = {
      props: (route: Route) => null,
    };
    

    See also

    https://github.com/Microsoft/TypeScript/issues/5711

    When trying to emit [the module], the compiler needs to write an object type literal... representing the shape of the module. But there isn't a name in scope that refers directly to [Route], so the type "cannot be named" and there's an error.

    If you add [a direct] import of [Route]... the error should go away.