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

如何根据Joi Schema中字段值动态添加额外字段?

如何在Joi中根据字段值动态添加额外字段

问题场景

现有一个Joi Schema,可根据type字段的值动态设置value的类型,运行正常:

Joi.object({
    name: Joi.string().required(),    
    params: Joi.array().items(Joi.object({
        type: Joi.string().valid("number", "string", "boolean").required(),
        key: Joi.string().required(),
        value: Joi.when("type", {
            is: "number",
            then: Joi.number()
        }).when("type", {
            is: "string",
            then: Joi.string()
        }).when("type", {
            is: "boolean",
            then: Joi.boolean()
        }).allow(null).default(null)
    }))
})

现在需要实现:

  • 当type="string"时,为对象添加default字段(带默认值)
  • 当type="number"时,为对象添加min和max字段(带默认值)

尝试了以下代码,但未生效,既无报错也未生成预期的默认字段:

Joi.object({
    name: Joi.string().required(),
    params: Joi.array().items(Joi.object({
        key: Joi.string().required(),
        value: Joi.when("type", {
            is: "number",
            then: Joi.number()
        }).when("type", {
            is: "string",
            then: Joi.string()
        }).when("type", {
            is: "boolean",
            then: Joi.boolean()
        }).allow(null).default(null),
        type: Joi.string().valid("number", "string", "boolean").required(),
    }).when("type", {
        is: "number",
        then: Joi.object({
            min: Joi.number().default(0),
            max: Joi.number().default(100)
        })
    }).when("type", {
        is: "string",
        then: Joi.object({
            default: Joi.string().default("default-value"),
        })
    }))
})

正确实现方式

要根据其他字段值动态添加字段,核心是在对象内部使用.when()定义条件字段,或结合.concat()合并条件对象。推荐两种写法:

方法一:对象内部直接定义动态字段

把需要动态添加的字段用.when直接写在顶层对象中,Joi会根据type的值自动启用对应字段:

Joi.object({
    name: Joi.string().required(),
    params: Joi.array().items(
        Joi.object({
            key: Joi.string().required(),
            type: Joi.string().valid("number", "string", "boolean").required(),
            value: Joi.when("type", {
                is: "number",
                then: Joi.number()
            }).when("type", {
                is: "string",
                then: Joi.string()
            }).when("type", {
                is: "boolean",
                then: Joi.boolean()
            }).allow(null).default(null),
            // string类型专属字段:仅type为string时生效
            default: Joi.when("type", {
                is: "string",
                then: Joi.string().default("default-value"),
                otherwise: Joi.forbidden() // 非string类型禁止该字段
            }),
            // number类型专属字段:仅type为number时生效
            min: Joi.when("type", {
                is: "number",
                then: Joi.number().required().default(0),
                otherwise: Joi.forbidden()
            }),
            max: Joi.when("type", {
                is: "number",
                then: Joi.number().required().default(100),
                otherwise: Joi.forbidden()
            })
        })
    )
})

方法二:用.concat()合并模块化Schema

如果动态字段较多,可将不同类型的字段封装为独立Schema,再通过.when和.concat合并:

// 基础字段Schema
const baseParamSchema = Joi.object({
    key: Joi.string().required(),
    type: Joi.string().valid("number", "string", "boolean").required(),
    value: Joi.when("type", {
        is: "number",
        then: Joi.number()
    }).when("type", {
        is: "string",
        then: Joi.string()
    }).when("type", {
        is: "boolean",
        then: Joi.boolean()
    }).allow(null).default(null)
});

// number类型专属字段Schema
const numberParamSchema = Joi.object({
    min: Joi.number().required().default(0),
    max: Joi.number().required().default(100)
});

// string类型专属字段Schema
const stringParamSchema = Joi.object({
    default: Joi.string().default("default-value")
});

// 最终组合Schema
const schema = Joi.object({
    name: Joi.string().required(),
    params: Joi.array().items(
        baseParamSchema
            .when("type", {
                is: "number",
                then: Joi.object().concat(numberParamSchema)
            })
            .when("type", {
                is: "string",
                then: Joi.object().concat(stringParamSchema)
            })
    )
});

关键说明

  • 原代码失效原因:在对象外部调用.when时,Joi不会自动将条件对象的字段合并到原对象中,必须显式使用.concat或在对象内部定义条件字段。
  • otherwise: Joi.forbidden()可确保非对应类型时,动态字段无法被提交,避免脏数据。
  • 两种方法各有适用场景:方法一适合字段较少的情况,方法二更适合字段较多、需要模块化管理的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 10:50:38