我很难让自定义请求类型与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: No overload匹配此调用
我该如何解决这个问题?我做的每件事都不起作用,我也不完全确定为什么。
如果我在PrivateRequest
上使user
为空,我可以修复此错误,但这在技术上是不正确的,因为所有的私有路由都作为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" } });
}
}
由于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);
另一个选择是在处理程序中使用断言函数:
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" } });
}
}