TypeScript:自定义快速路由器上的自定义请求类型 (TS2769)

时间:2021-02-03 14:54:17

标签: typescript express

我很难让自定义请求类型与 TypeScript 完美配合。

在我的应用中有公共和私人路线。

公共路由使用 Express 中的 Request 类型。私有路由使用自定义的 PrivateRequest 类型,它扩展了 Express 的 Request 类型;看起来像这样:

import type { Request } from "express";
import type * as Cookies from "cookies";

export type PrivateRequest = Request & {
  user: User;
  cookies: Cookies;
}

公共和私有路由的路由如下所示:

const publicRouter = express.Router();
const privateRouter = express.Router();

privateRouter.use([userSession]);

publicRouter.post("/login", login);
privateRouter.get("/api/user", user);

这是一个私有路由的例子,它使用了 PrivateRequest 类型,这里的 TypeScript 没有问题。

export default async function user(req: PrivateRequest, res: Response) {
  try {
    res.json({ test: true });
  } catch (err) {
    console.error(err);
    res.status(500).json({ errors: { server: "Server error" } });
  }
}

问题在于私人路线,例如:

privateRouter.get("/api/user", user);

我从 TypeScript 返回的私有定义路由的具体错误是:

<块引用>

TS2769:没有与此调用匹配的过载

我该如何解决这个问题?我尝试的所有方法都不起作用,我不完全确定原因。

如果我在 user 上使 PrivateRequest 可以为空,我可以修复这个错误,但这在技术上是不正确的,因为所有私有路由都被保证为 req 对象上的用户,因为userSession 中间件要么以 401 响应,要么将用户添加到 req 对象以用于后续私有路由。下面是我的 userSession 中间件的外观示例:

export default async function userSession(
  req: Request,
  res: Response,
  next: NextFunction
) {
  try {
    req.cookies = new Cookies(req, res);

    // [...] authentication and user entity fetching (throws if either one fails)

    if (user === null) {
      res.status(401).json({ errors: { server: "Unauthorised" } });
    } else {
      // @ts-ignore
      req.user = user;
      next();
    }
  } catch {
    res.status(401).json({ errors: { server: "Unauthorised" } });
  }
}

1 个答案:

答案 0 :(得分:1)

由于 Express 的 get 被定义为接受接受 Request 而不是 PrivateRequest 的处理程序,因此您必须向 TypeScript 保证这没问题,您知道 user并且 cookies 将被添加到请求对象中。 get 的定义方式,TypeScript 必须假设您的处理程序只会获得 Request,而不是 PrivateRequest

一种方法是使用类型断言,可能是在一个实用函数中:

const addPrivateGet = (path: string, handler: (req: PrivateRequest, res: Response)) => {
    privateRouter.get(path, handler as unknown as (req: Request, res: Response) => void);
};
// ...
addPrivateGet("/api/user", user);

另一种选择是在处理程序中使用 assertion function

function assertIsPrivateRequest(req: Request): asserts req is PrivateRequest {
    if (!("user" in req)) {
        throw new AssertionError(`Invalid request object, missing 'user'`);
    }
    if (!("cookies" in req)) {
        throw new AssertionError(`Invalid request object, missing 'cookies'`);
    }
}

然后在您的处理程序中:

export default async function user(req: Request, res: Response) {
// Note −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−^
  try {
    assertIsPrivateRequest(req); // <==
    // You can use `req.user` and `req.cookies` here
    res.json({ test: true });
  } catch (err) {
    console.error(err);
    res.status(500).json({ errors: { server: "Server error" } });
  }
}