鸿蒙 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 更新了什么
这次主要增加了五组能力:
- 统一错误码 :所有内置规则都返回稳定
code; - 国际化:内置中文、英文,支持自定义语言包;
- 字段标签 :通过
.label()展示业务字段名,不改变错误 path; - Schema 组合 :新增
literal、union、nullable; - 数据解析 :新增
default、transform、parse、parseAsync。
同时修复了 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 或具体业务场景。
项目链接
- GitHub:github.com/lxshwyan/ha...
- OHPM:ohpm.openharmony.cn/#/cn/detail...
- 安装:
ohpm i @hmkit/validator
觉得有用的话,欢迎点个 Star。这个项目我会继续维护,也会把后续鸿蒙开源项目的工程化、测试和发布经验一起沉淀下来。🙌