TypeScript+Express:查询参数为何为string?如何匹配定义类型?
Express + TypeScript 中实现类型一致的查询参数
问题场景
我尝试在TypeScript、Express及@types/express中使用类型化查询参数,编写代码如下:
import express, { Request, Response } from "express"; const app = express(); app.get('/', (request: Request<{}, {}, {}, { page: number }>, response: Response) => { response.send(`value: ${request.query.page}, type: ${typeof request.query.page} `); }); app.listen(3000);
但实际运行时,无论如何定义查询参数的类型,request.query.page的实际类型始终为string,测试结果:
$ curl "http://localhost:3000/?page=7" value: 7, type: string
我不想通过强制类型转换处理,希望通过类型系统让查询参数类型与方法签名定义一致。
使用的包版本:
typescript 4.9.4 express: 4.18.2 @types/express: 4.17.15
tsconfig.json已启用"strict": true。
解决方案
核心原因
HTTP协议中,所有查询参数本质都是字符串(或字符串数组),TypeScript的Request泛型只是静态类型标注,不会在运行时自动转换类型。要实现类型一致,必须在运行时做类型转换和验证。
方案1:自定义类型转换中间件
自己编写中间件,将指定查询参数转换为目标类型,并做基础验证:
import express, { Request, Response, NextFunction } from "express"; interface TypedRequestQuery { page?: number; } // 类型转换中间件 const parsePageQuery = (req: Request<{}, {}, {}, TypedRequestQuery>, res: Response, next: NextFunction) => { const pageStr = req.query.page; if (pageStr !== undefined) { const pageNum = parseInt(pageStr, 10); // 验证是否为有效数字 if (!isNaN(pageNum) && Number.isInteger(pageNum) && pageNum > 0) { req.query.page = pageNum; } else { return res.status(400).send('Invalid page parameter: must be a positive integer'); } } next(); }; const app = express(); app.get('/', parsePageQuery, (request: Request<{}, {}, {}, TypedRequestQuery>, response: Response) => { response.send(`value: ${request.query.page}, type: ${typeof request.query.page}`); }); app.listen(3000);
这样处理后,request.query.page在运行时会是number类型,同时TypeScript静态检查也能匹配。
方案2:使用Schema验证库(如Zod)
用专门的验证库做类型转换和校验,同时自动生成TypeScript类型:
import express, { Request, Response } from "express"; import { z } from "zod"; // 定义查询参数Schema const PageQuerySchema = z.object({ page: z.coerce.number().int().positive().optional() }); // 提取Schema对应的TypeScript类型 type PageQuery = z.infer<typeof PageQuerySchema>; const app = express(); app.get('/', (request: Request<{}, {}, {}, PageQuery>, response: Response) => { // 运行时验证并转换类型 const result = PageQuerySchema.safeParse(request.query); if (!result.success) { return res.status(400).send(result.error.issues); } const { page } = result.data; response.send(`value: ${page}, type: ${typeof page}`); }); app.listen(3000);
z.coerce.number()会自动将字符串转换为数字,转换失败时会抛出验证错误,同时TypeScript能通过z.infer获得准确的类型。
方案3:类型守卫函数
如果不需要复杂验证,可以用类型守卫来断言类型:
import express, { Request, Response } from "express"; interface PageQuery { page?: number; } // 类型守卫函数 function isPageQuery(query: unknown): query is PageQuery { if (typeof query !== 'object' || query === null) return false; const pageStr = (query as PageQuery).page; if (pageStr === undefined) return true; const pageNum = parseInt(pageStr as string, 10); return !isNaN(pageNum) && Number.isInteger(pageNum); } const app = express(); app.get('/', (request: Request, response: Response) => { if (!isPageQuery(request.query)) { return res.status(400).send('Invalid query parameters'); } // 这里request.query会被断言为PageQuery类型,page是number response.send(`value: ${request.query.page}, type: ${typeof request.query.page}`); }); app.listen(3000);
内容的提问来源于stack exchange,提问作者bergoid
相关产品推荐
相关产品推荐

