label / description / messages / error 使用指南

📋 快速对比

属性用途显示位置示例
.label(text)字段显示名错误消息、表单标题、导出文档标题邮箱地址
.description(text)字段说明表单提示、API 文档、导出文档说明用于登录和接收通知
.messages(map)自定义验证消息验证失败时返回的错误消息{ required: '{{#label}}不能为空' }
.error(map).messages(map) 的别名.messages() 完全相同{ pattern: '格式不正确' }

一句话记忆:

  • .label() 告诉错误消息“这个字段叫什么”。
  • .description() 告诉页面或文档“这个字段是干什么的”。
  • .messages() 告诉验证器“某类错误要显示什么话”。
  • .error() 只是 .messages() 的短别名,适合想写得更短时使用。

🎯 详细说明

label(标签)

作用: 在错误消息中替换字段名

使用场景:

  • 让错误消息更友好
  • 中文化字段名
  • 简化技术字段名

示例:

// 不使用label
email: 'email!'
// 错误消息: "email is required"  ❌ 不友好

// 使用label
email: s('email!').label('邮箱地址')
// 错误消息: "邮箱地址不能为空"  ✅ 友好

完整示例:

const schema = s({
  userEmail: s('email!').label('用户邮箱')
    .messages({
      'required': '{{#label}}不能为空',  // 使用label值
      'format': '{{#label}}格式不正确'
    })
});

// 验证失败时:
// 错误: "用户邮箱不能为空"
// 错误: "用户邮箱格式不正确"

description(描述)

作用: 提供字段的详细说明

使用场景:

  • 表单输入提示
  • API文档生成
  • Schema文档
  • 帮助用户理解字段用途

示例:

email: s('email!').label('邮箱地址')
  .description('用于登录和接收系统通知')

在表单中使用:

<div class="form-field">
  <label>邮箱地址</label>  <!-- 来自 label -->
  <input type="email" />
  <span class="hint">用于登录和接收系统通知</span>  <!-- 来自 description -->
</div>

在导出 / 文档工具中:

{
  "email": {
    "type": "string",
    "format": "email",
    "_label": "邮箱地址",          // label 在 schema-dsl 内部以 _label 保存
    "description": "用于登录和接收系统通知"  // 来自 description
  }
}

SchemaUtils.toMarkdown()、导出器或你自己的表单渲染层,通常会再把 _label 映射成展示标题。


messages(自定义错误消息)

作用:覆盖某些验证规则失败时的提示文案。

使用场景

  • 必填、格式、长度、范围、正则等错误需要业务化文案
  • 需要在消息中引用字段标签 {{#label}}
  • 需要统一接口错误消息风格
import { s, validate } from 'schema-dsl/pure';

const schema = s({
  email: s('email!')
    .label('邮箱地址')
    .messages({
      required: '{{#label}}不能为空',
      format: '请输入有效的{{#label}}'
    })
});

validate(schema, {}).errors[0].message; // 邮箱地址不能为空
validate(schema, { email: 'bad' }).errors[0].message; // 请输入有效的邮箱地址

常用 key:

key含义常见触发
required必填失败字段缺失或为空
formatJSON Schema format 失败emailurluuid
pattern正则失败.pattern() 或内置 pattern 类型
min / max数字范围失败number:18-120.min().max()
string.min / string.max字符串长度失败string:3-32
array.min / array.max数组长度失败array:1-10<string>

如果不确定具体 key,可以先查看验证结果里的 errors,再按返回的错误类型覆盖。


error(messages 的别名)

作用:与 .messages() 完全相同,内部都会写入 _customMessages

const schema = s({
  username: s('string:3-32!')
    .label('用户名')
    .pattern(/^[a-zA-Z0-9_]+$/)
    .error({
      pattern: '{{#label}}只能包含字母、数字和下划线'
    })
});

建议:

  • 团队喜欢语义清楚时,用 .messages()
  • 单个字段只想快速写错误文案时,用 .error()
  • 同一个字段上不要同时混用 .messages().error() 来表达不同含义,因为它们只是同一件事。

💡 最佳实践

1. label 是必需的(用户可见字段)

const schema = s({
  // ✅ 好:所有用户可见字段都有label
  username: s('string:3-32!').label('用户名'),
  email: s('email!').label('邮箱地址'),
  password: s('string:8-64!').label('密码'),
  
  // ⚠️ 可以:内部字段可以不用label
  userId: 'uuid!',
  createdAt: 'date!'
});

2. description 是可选的(需要说明时使用)

const schema = s({
  // ✅ 复杂字段:添加description
  apiKey: s('string:32!').label('API密钥')
    .description('用于调用第三方API,请妥善保管'),
  
  // ✅ 简单字段:不需要description
  name: s('string:1-50!').label('姓名'),
  
  // ✅ 有特殊要求的字段:添加description
  password: s('string:8-64!').label('密码')
    .description('必须包含大小写字母和数字')
    .pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).+$/)
});

3. 组合使用示例

const userSchema = s({
  // 完整的字段定义
  email: s('email!').label('邮箱地址')                    // 错误消息中显示
    .description('用于登录和接收通知')    // 表单提示/文档
    .messages({
      'required': '{{#label}}不能为空',
      'format': '请输入有效的{{#label}}'
    }),
  
  // 简单字段
  age: s('number:18-120').label('年龄'),
  
  // 复杂字段
  bio: s('string:500').label('个人简介')
    .description('介绍你自己,最多500字'),
  
  // 内部字段(无需label/description)
  userId: 'uuid!',
  createdAt: 'date!'
});

📊 使用场景对比

场景labeldescription
错误消息✅ 必需❌ 不使用
表单标签✅ 推荐⚠️ 可选
表单提示❌ 不使用✅ 推荐
API文档✅ 作为title✅ 作为说明
Schema文档✅ 字段名✅ 字段说明
内部字段⚠️ 可选⚠️ 可选

🎨 实际效果

验证错误显示

// Schema定义
const schema = s({
  email: s('email!').label('邮箱地址')
    .messages({
      'required': '{{#label}}不能为空',
      'format': '{{#label}}格式不正确'
    })
});

// 验证空值
validator.validate(schema, { email: '' });
// 错误: "邮箱地址不能为空"  ← 使用了label

// 验证错误格式
validator.validate(schema, { email: 'invalid' });
// 错误: "邮箱地址格式不正确"  ← 使用了label

表单渲染

const schema = s({
  password: s('string:8-64!').label('登录密码')
    .description('8-64位,包含大小写字母和数字')
    .pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).+$/)
});

// 渲染为HTML
<div class="form-field">
  <label>登录密码</label>                              ← label
  <input type="password" />
  <span class="hint">8-64位,包含大小写字母和数字</span>  ← description
</div>

✅ 总结

label

  • 必需性: 用户可见字段推荐使用
  • 用途: 让错误消息更友好
  • 位置: 错误消息、表单标签
  • 格式: 简短的名词(2-6个字)

description

  • 必需性: 可选,需要说明时使用
  • 用途: 帮助用户理解字段用途
  • 位置: 表单提示、API文档
  • 格式: 完整的句子或短语

推荐组合

// 最小配置(简单字段)
name: s('string:1-50!').label('姓名')

// 标准配置(常规字段)
email: s('email!').label('邮箱地址')
  .messages({ 'format': '请输入有效的{{#label}}' })

// 完整配置(复杂字段)
apiKey: s('string:32!').label('API密钥')
  .description('用于调用第三方API,请妥善保管')
  .pattern(/^[A-Za-z0-9]{32}$/)
  .messages({
    'required': '{{#label}}不能为空',
    'pattern': '{{#label}}格式不正确'
  })

记住: label用于错误消息和展示标题来源,description用于帮助说明!


对应示例文件

示例入口: label-vs-description.ts
说明: 直接展示 _label / description 在 schema 中的实际落点,以及验证错误如何消费 label