【问题标题】:Extending express.Request in typescript在打字稿中扩展 express.Request
【发布时间】:2021-08-10 03:33:12
【问题描述】:

我可以很容易地在 typescript 中扩展 express.Request,例如:

interface MyRequest extends express.Request {
    userId: string;
}

但是,如果我编写一个使用它的中间件函数,例如:

const myMiddlewareFn = (req: MyRequest, res: express.Response) => {
    req.userId; // <-- this works as a string
}
app.get('/user', myMiddlewareFn) // <-- this does NOT type-check because MyRequest is more narrow than express.Request.

我看到很多关于如何扩展 express.Request 的讨论。我怎样才能在我的路线中正确地进行类型检查?

目前我正在做这样的事情:

/**
 * @param allowedRoles see HasuraCustomClaims
 * @param handlerFn the final business logic
 * @returns an array of middleware, correctly typed for the handlerFn
 */
export function addApiAuthMiddleware(
    allowedRoles: string[],
    ...handlerFns: (
        | ((req: OTApiAuthorizedRequest, res: Response) => Promise<void>)
        | ((req: OTApiValidatedAndAuthorizedRequest<any>, res: Response) => Promise<void>)
    )[]
): express.RequestHandler[] {
    return [
        decodeIdToken,
        guardRequestHandler(createRoleAuthMiddleware(allowedRoles)),
        ...handlerFns.map((handlerFn) => guardRequestHandler(handlerFn)),
    ];
}

/**
 * This typecasting is a *little* jank.
 *
 * @param middlewareOrHandlerFn either api middleware or a final api handler function
 * @returns
 */
export function guardRequestHandler<T extends Request>(
    middlewareOrHandlerFn:
        | ((req: T, res: Response, next: express.NextFunction) => Promise<void>)
        | ((req: T, res: Response) => Promise<void>),
): express.RequestHandler {
    return (middlewareOrHandlerFn as unknown) as express.RequestHandler;
}

// this below type-checks
app.get('/user', ...addApiAuthMiddleware(['admin'], userHandlerFn));

有没有更好的办法?

【问题讨论】:

    标签: node.js typescript express


    【解决方案1】:

    设置你的自定义属性是可选属性。

    interface MyRequest extends express.Request {
        userId?: string; // userId is optional
    }
    

    在您的中间件或请求处理程序中,您始终需要确保userId 不是未定义的。

    const myMiddlewareFn = (req: MyRequest, res: express.Response) => { // missing next function ?
        if (req.userId) {
            // do something with userId
            req.userId; // <-- this works as a string
        }
    }
    

    【讨论】:

    • 这是有道理的并且满足类型检查器的要求,但在我看来,我获得好的类型安全中间件而不需要进行空值检查不是主要的好处吗?例如想象一下我在中间件之前使用了 joi 验证。有些是必填字段,有些不是。在我的 Req 始终是 Partial<...> 的情况下,它并没有真正捕获“必需”,对吧?
    • 如何确保在中间件之前使用 Joi 验证?
    • 我不能!我猜你的意思是我们应该做一个解决方案,为了进行类型检查,我确实需要用一些类型保护来包装我的控制器是否进行验证?
    【解决方案2】:

    您可以将 app.get('/user', myMiddlewareFn) 更改为 app.get('/user', myMiddlewareFn as any)app .get('/user', myMiddlewareFn) 这些是相同的,并且都是编译时强制转换不是运行时

    【讨论】:

    • 1. app.get('/user', myMiddlewareFn) 引发类型错误,不是吗? 2. app.get('/user', myMiddlewareFn as any) 这对我来说似乎有点吓人,我知道它有效,但问题是开发人员现在可能会不小心搞砸 myMiddlewareFn(例如,如果他们现在想要接受下一个,即使它不存在!?!?)或者他们只是搞砸了等等,any 将掩盖这些错误。
    猜你喜欢
    • 2019-09-18
    • 2019-03-22
    • 2020-07-07
    • 2020-11-18
    • 2019-05-04
    • 2021-06-18
    • 2016-07-04
    • 1970-01-01
    相关资源
    最近更新 更多