如何动态创建路由?

How to dynamically create routes?

我正在尝试通过遍历称为路由组的对象数组来向我的 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[];
};

这是我的路由器 class:

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 类型的方法 属性 从字符串更改为有效的索引签名?这就是我需要做的吗?

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

(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);

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


P.S。我建议将 oakRouter 重命名为 OakRouter。 Class 和类型名称通常以 JavaScript/TypeScript.

中的大写字母开头