鸿蒙 ArkTS 校验库 @hmkit/validator 0.5.0:国际化、错误码和 Schema 组合都来了

鸿蒙 ArkTS 校验库 @hmkit/validator 0.5.0:国际化、错误码和 Schema 组合都来了

前段时间,我开源了一个鸿蒙 ArkTS 声明式校验库 @hmkit/validator。这次 0.5.0 不只是补几个正则,而是把错误体系、国际化、默认值、类型转换和 Schema 组合能力完整补上了。

如果你在 HarmonyOS NEXT / OpenHarmony 项目里写过注册、登录、实名认证、订单或者动态表单,大概率遇到过这些问题:

  • 错误提示散落在页面里,中英文切换时到处改文案;
  • UI 只能依赖中文错误字符串,无法稳定埋点或映射样式;
  • 后端给的是字符串,业务真正需要的是 number、默认值或可空类型;
  • 一个字段可能接受多种值,最后又写回一堆 if/else
  • 嵌套表单一复杂,错误路径、异步校验和转换逻辑很容易互相打架。

所以这次我把它从"校验规则集合"继续往"ArkTS Schema 工具"方向推进了一步。

项目地址:github.com/lxshwyan/ha...

先看 0.5.0 更新了什么

这次主要增加了五组能力:

  1. 统一错误码 :所有内置规则都返回稳定 code
  2. 国际化:内置中文、英文,支持自定义语言包;
  3. 字段标签 :通过 .label() 展示业务字段名,不改变错误 path;
  4. Schema 组合 :新增 literalunionnullable
  5. 数据解析 :新增 defaulttransformparseparseAsync

同时修复了 Object/Array 解析时重复执行校验或转换的问题,并增加了一整组稳定性回归测试。

如果你错过了上一版,0.4.0 还增加了 ArkTS 泛型类型保留、validateAllAsync() / isValidAsync() 整表异步校验、严格 ISO 日期模式和 VIN 第 9 位校验位。0.5.0 是在这套基础上继续补齐错误体系和 Schema 数据处理能力。

一、错误不再只是中文字符串

以前校验失败主要拿到 path + message

typescript 复制代码
{
  path: 'email',
  message: '请输入正确的邮箱地址',
}

展示没有问题,但如果 UI 想根据错误类型做埋点、图标或者交互,就只能判断中文字符串。

0.5.0 为内置规则增加了稳定错误码:

typescript 复制代码
import { v, ErrorCode } from '@hmkit/validator';

const result = v.string().email().validate('not-an-email');

if (!result.valid && result.errors[0].code === ErrorCode.EMAIL) {
  // 埋点、映射 UI、统计错误类型
}

错误结果现在类似这样:

typescript 复制代码
{
  path: '',
  code: 'email',
  message: '请输入正确的邮箱地址',
}

message 负责给人看,code 负责给程序判断,两者终于分开了。

二、内置中英文,也支持自己的语言包

切换英文只需要一行:

typescript 复制代码
import { v } from '@hmkit/validator';

v.setLocale('en-US');

const result = v.string()
  .label('Email')
  .required()
  .email()
  .validate('bad');

// Email: Invalid email address

切回中文:

typescript 复制代码
v.setLocale('zh-CN');

也可以注册业务自己的语言包:

typescript 复制代码
v.addLocale('my-app', {
  'required': '{label}不能为空',
  'string_min': '{label}至少需要 {min} 个字符',
});

v.setLocale('my-app');

目前模板支持 {label}{min}{max}{values}{expected} 等参数。

这里有两个设计细节:

  • 语言在真正执行 validate() / parse() 时解析,所以已经创建的 Schema 也能响应语言切换;
  • 开发者显式传入的自定义消息始终优先,升级后不会突然被内置语言包覆盖。

三、.label() 解决"字段路径"和"展示名称"冲突

表单字段可能叫 idNo,但用户看到的应该是"身份证号码"。

typescript 复制代码
const result = v.string()
  .label('身份证号码')
  .required()
  .idCard()
  .validate('123');

错误信息会显示:

text 复制代码
身份证号码:请输入正确的身份证号

但是错误 path 仍然可以保持 idNo,不会影响表单状态和接口字段映射。

四、literal、union、nullable:组合 Schema 更自然

精确值 literal

typescript 复制代码
const status = v.literal('ready');

status.validate('ready'); // 通过
status.validate('done');  // code = literal

多候选 union

typescript 复制代码
const status = v.union<string>([
  v.literal('draft'),
  v.literal('done'),
]);

status.validate('draft'); // 通过
status.validate('other'); // code = union

union 会按候选顺序匹配,任意一个通过就直接成功;全部失败时只返回一个稳定的 union 错误,不会把所有候选错误一股脑丢给 UI。

显式可空 nullable

typescript 复制代码
const nickname = v.string().required().nullable();

nickname.validate('小明'); // 通过
nickname.validate(null);   // 通过
nickname.validate(undefined); // 不通过,required 仍然有效

nullable() 只额外接受显式 null,不会把字段缺失的 undefined 混为一谈。

五、default 和 transform:校验之后直接拿到业务值

以前 validate() 只回答"对不对",但真实表单经常还需要:

  • 字段没传时补默认值;
  • 把输入框字符串转换为数字;
  • 递归处理对象和数组中的数据。

0.5.0 增加了 parse() / parseAsync()

默认值

typescript 复制代码
const nickname = v.string().nullable().default('游客');

nickname.parse(undefined);
// { success: true, value: '游客', errors: [] }

nickname.parse(null);
// { success: true, value: null, errors: [] }

default() 只处理 undefined,不会吞掉用户显式传入的 null

类型转换

typescript 复制代码
const age = v.string()
  .required()
  .transform<number>(
    (value: string): number => parseInt(value, 10),
    '年龄转换失败',
  );

const result = age.parse('18');
// result.value 为 number 类型的 18

如果转换函数抛出异常,不会让整个应用崩溃,而是返回稳定的 transform 错误。

嵌套对象自动处理

typescript 复制代码
const userSchema = v.object({
  'name': v.string().default('游客'),
  'age': v.string().transform<number>(
    (value: string): number => parseInt(value, 10),
  ),
});

const input: Record<string, Object> = { 'age': '18' };
const parsed = userSchema.parse(input);

// parsed.value:
// {
//   name: '游客',
//   age: 18,
// }

对象和数组可以递归应用默认值和 transform,失败时仍然保留类似 items.0.email 的完整错误路径。

六、这次还专门修了一个"测试全绿也不容易发现"的问题

第一版实现里,object.parse()array.parse() 会先完整校验一次,再对子字段执行解析。

普通的 min()email() 看不出问题,但如果里面是:

  • 远程用户名查重;
  • 带计数或状态的自定义规则;
  • 有副作用的 transform;

就可能执行两遍,甚至发出两次网络请求。

0.5.0 最终改成了单次遍历解析

  • 同步、异步规则都只执行一次;
  • 异步对象字段仍然并发执行;
  • 错误结果继续按照 Schema 声明顺序稳定输出;
  • 对象字段和数组下标路径完整保留。

这类问题只看覆盖率数字不一定能发现,所以我新增了专门的执行次数和重复运行测试。

七、119 项自动化测试,连续重复运行验证

当前版本有 119 项可公开复现的 Hypium 自动化测试,覆盖:

  • String / Number / Boolean / Enum / Date / Array / Object;
  • 手机号、身份证、银行卡、信用代码、VIN 等中国规则;
  • 国际化、字段标签和全部错误码;
  • literal / union / nullable / default / transform;
  • 同步与异步一致性;
  • 嵌套错误路径和错误聚合顺序;
  • 自定义回调异常、单次执行和重复运行稳定性;
  • ArkUI FormValidator 的字段与整表校验。

发布前测试连续运行三轮,结果完全一致:

text 复制代码
Tests:      119/119 passed
Lines:      93.87%
Functions:  86.11%
Branches:   90.11%

CI 设置了 90% 行、80% 函数、80% 分支覆盖率门禁,编译失败、断言失败、陈旧测试报告和覆盖率退化都会阻止发布。

八、安装与使用

bash 复制代码
ohpm install @hmkit/validator

指定 0.5.0:

bash 复制代码
ohpm install @hmkit/validator@0.5.0

如果 OHPM 页面仍显示旧版本,说明 0.5.0 尚在平台审核中;审核通过后即可安装指定版本。

typescript 复制代码
import { v, ErrorCode, FormValidator } from '@hmkit/validator';

项目保持零依赖、纯 ArkTS,校验逻辑不绑定 UI;需要 ArkUI 表单联动时再使用 FormValidator

后续计划

0.5.0 把 Schema 和错误体系打好了基础,后面准备继续做:

  • ArkUI 防抖异步校验;
  • blur/change/submit 等触发策略;
  • submitting、touched、dirty 状态;
  • 字段依赖与联动校验;
  • 中国规则按需引入和自定义规则插件。

如果你正在做鸿蒙表单、实名认证、订单录入或者后台管理项目,欢迎试用,也欢迎提 issue、PR 或具体业务场景。

项目链接

觉得有用的话,欢迎点个 Star。这个项目我会继续维护,也会把后续鸿蒙开源项目的工程化、测试和发布经验一起沉淀下来。🙌

相关推荐
wenruozhu1 小时前
【面试题解】 Vue 响应式原理
前端·面试
01_ice1 小时前
前端学习JS(3)
前端·javascript·学习
无懈可击1 小时前
Vue 低代码可视化 AI 表单设计器 FcDesigner v3.5 版本发布!
前端·vue.js·前端框架
哈__1 小时前
极空间部署 Spug:集中管理主机、批量执行脚本与 Web 终端
前端
Virony1 小时前
React基础知识
前端·react.js·前端框架
Hilaku1 小时前
外包前端和大厂前端,写的代码到底有什么本质区别?
前端·javascript·程序员
liangshanbo12151 小时前
React Hooks 为什么不能在条件分支/循环中调用?
前端·react.js·前端框架
豆角焖肉1 小时前
Layui+Layer 弹出层实现 CRUD:AJAX 无刷新新增修改删除后台实战
前端·ajax·layui
wordbaby1 小时前
npm / yarn / pnpm:别再靠感觉选了
前端·前端工程化