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

如何在TypeScript中使用Zod验证测试null值且避免类型错误?

问题:Zod验证中测试Null值时的TypeScript错误

背景代码

后端接口的Zod验证配置:

app.post(
    '/api/users',
    validateRequest({ 
        body: z.object({
            username: z.string(),
            email: z.string(),
            password: z.string(),
        })
    }),
    async (req, res) => {
        const hashedPassword = await argon2.hash(req.body.password);
        await User.create({
            ...req.body,
            password: hashedPassword,
        });
        res.status(201).send({ message: 'User created' });
    },
);

测试代码与报错

尝试测试username为null的场景:

it('should return a 400 status code when username is null', async () => {
    const response = await request(app)
        .post('/api/users')
        .send({
            username: null,
            email: 'user@admin.com',
            password: 'pass4User',
        });
    expect(response.statusCode).toBe(400);
});

触发TypeScript错误:

Type 'null' is not assignable to type 'string'

解决方案

1. 用类型断言绕过TS检查

直接给发送的对象断言为any,让TS跳过类型校验:

it('should return a 400 status code when username is null', async () => {
    const response = await request(app)
        .post('/api/users')
        .send({
            username: null,
            email: 'user@admin.com',
            password: 'pass4User',
        } as any);
    expect(response.statusCode).toBe(400);
});

2. 定义测试专用的无效类型

如果需要多次测试不同无效值,可以定义一个兼容无效情况的类型:

type InvalidUserInput = Partial<Record<keyof {username: string; email: string; password: string}, string | null | undefined>>;

it('should return a 400 status code when username is null', async () => {
    const invalidInput: InvalidUserInput = {
        username: null,
        email: 'user@admin.com',
        password: 'pass4User',
    };
    const response = await request(app)
        .post('/api/users')
        .send(invalidInput);
    expect(response.statusCode).toBe(400);
});

3. 基于Zod类型扩展测试用类型

利用Zod的z.infer获取原本的输入类型,再扩展允许字段为null/undefined:

import { z } from 'zod';

const UserSchema = z.object({
    username: z.string(),
    email: z.string(),
    password: z.string(),
});

type ValidUserInput = z.infer<typeof UserSchema>;
type TestUserInput = Partial<Record<keyof ValidUserInput, ValidUserInput[keyof ValidUserInput] | null | undefined>>;

it('should return a 400 status code when username is null', async () => {
    const testInput: TestUserInput = {
        username: null,
        email: 'user@admin.com',
        password: 'pass4User',
    };
    const response = await request(app)
        .post('/api/users')
        .send(testInput);
    expect(response.statusCode).toBe(400);
});

4. 用unknown类型中转

把测试输入声明为unknown再转换,避免TS校验:

it('should return a 400 status code when username is null', async () => {
    const testInput: unknown = {
        username: null,
        email: 'user@admin.com',
        password: 'pass4User',
    };
    const response = await request(app)
        .post('/api/users')
        .send(testInput as any);
    expect(response.statusCode).toBe(400);
});

这些方法的核心都是在测试场景下,绕过TS对有效输入的类型限制——毕竟我们要测试的就是无效输入的处理逻辑。

内容的提问来源于stack exchange,提问作者Stanley Ulili

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 13:51:15