You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 10:50:26