【问题标题】:How to dynamically create routes?如何动态创建路由?
【发布时间】:2021-09-17 21:06:30
【问题描述】:

我正在尝试通过迭代称为路由组的对象数组来动态地将路由添加到我的 api。一个路由组可以有多个路由。

这是我的类型(RouterContext 类型来自 Oak 中间件框架):

// routes/mod.ts
type Route = {
  method: string;
  path: string;
  handler: (ctx: RouterContext) => Promise<void>;
};

export type RouteGroup = {
  group: {
    prefix: string;
  };
  routes: Route[];
};

这是我的路由器类:

export class Router {
  // imported Oak's Router class as oakRouter
  router: oakRouter;

  constructor() {
    this.router = new oakRouter({ prefix: "/api" });
  }

  register(): void {
    this._createRoutes(routeGroups);
  }

  private _createRoutes(routeGroups: RouteGroup[]) {
    routeGroups.forEach(({ group, routes }) => {
      routes.forEach(({ method, path, handler }) => {
        this.router[method](group.prefix + path, handler); // <-- this.router[method] is underlined with an error
      });
    });
  }
}

典型的路线如下所示:

router.get("/", (ctx) => {
    ctx.response.body = "Welcome to My Oak App.";
});

但是当我使用括号表示法在 _createRoutes() 中动态添加我想要使用的 http 方法时,我收到以下错误:

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Router<RouteParams, Record<string, any>>'.
  No index signature with a parameter of type 'string' was found on type 'Router<RouteParams, Record<string, any>>'.deno-ts(7053)

如何将 Route 类型的方法属性从字符串更改为有效的索引签名?这甚至是我需要做的吗?

【问题讨论】:

    标签: typescript deno oak


    【解决方案1】:

    如果您确信自己比 TypeScript 编译器更了解,最简单且最不安全的方法是将您的路由器函数转换为 any

    (this.router[method] as any)(group.prefix + path, handler);
    

    (<any>this.router[method])(group.prefix + path, handler);
    

    这绕过了很多通常不是我们想要的类型检查。


    另一种方法是为 Oak Router 支持的每个方法使用类型保护(例如,通过 switch 语句),这是类型安全的,但确实重复了一些代码:

    const args = [group.prefix + path, handler] as const;
    switch (method) {
      case "all":
        this.router[method](...args);
        break;
      case "delete":
        this.router[method](...args);
        break;
      case "get":
        this.router[method](...args);
        break;
      case "head":
        this.router[method](...args);
        break;
      case "options":
        this.router[method](...args);
        break;
      case "patch":
        this.router[method](...args);
        break;
      case "post":
        this.router[method](...args);
        break;
      case "put":
        this.router[method](...args);
        break;
    }
    

    第三种选择是枚举Route.method 中支持的方法函数名称,然后告诉TypeScript 编译器将所有函数视为相同的函数类型(选择任何函数名称,例如get):

    type Route = {
      method:
        | "all"
        | "delete"
        | "get"
        | "head"
        | "options"
        | "patch"
        | "post"
        | "put";
      path: string;
      handler: (ctx: RouterContext) => Promise<void>;
    };
    

    然后在_createRoutes里面:

    (this.router[method] as oakRouter["get"])(group.prefix + path, handler);
    

    这比第一种方法更安全,但比第二种方法更不安全。


    附:我建议将oakRouter 重命名为OakRouter。在 JavaScript/TypeScript 中,类和类型名称通常以大写字母开头。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-07-08
      • 2022-11-12
      • 2023-04-11
      • 2019-09-09
      • 2018-09-19
      • 2021-12-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多