开发鸿蒙应用时,表单里显示的是“数量”,拿到的却常常是字符串;接口返回的布尔值,有时又是 "false"。

如果直接转换,可能碰到这些结果:

Number('');        // 0
Boolean('false');  // true

转换成功,并不代表符合业务预期。订单里没有填写数量,不能因为转换后得到 0,就当作已经填写;接口返回 "false",也不应该被当作 true。

我维护的开源库 @hmkit/validator,除了手机号、身份证等字段规则,也支持数据预处理、类型转换、对象与数组校验,以及按字段路径返回错误。

这篇以一个订单录入场景为例,演示如何把原始输入处理成业务需要的数据。示例使用已在 OHPM 上架的 1.4.0,文中的转换与嵌套校验能力在此前版本中已经提供。

1. 安装与最小示例

在项目使用的模块目录中执行:

ohpm install @hmkit/validator@1.4.0

先校验一个手机号:

import { v } from '@hmkit/validator';

const phoneRule = v.string()
  .required('请输入手机号')
  .phone('手机号格式不正确');

const checked = phoneRule.validate('123');
console.info(JSON.stringify(checked));

结果包含 valid 和 errors,不需要为正常的校验失败编写 try/catch。

对于简单输入,可以直接读取错误提示;字段变多后,则可以把规则组合成对象 Schema。

2. 类型转换要明确接受哪些输入

库提供了 coerceNumber()、coerceBoolean() 等转换入口,通过 parse() 获取结果:

import { v } from '@hmkit/validator';

const count = v.coerceNumber().parse('12');
console.info(JSON.stringify(count));
// { "success": true, "value": 12, "errors": [] }

const enabled = v.coerceBoolean().parse('false');
console.info(JSON.stringify(enabled));
// { "success": true, "value": false, "errors": [] }

几种常见输入的行为如下:

规则输入结果
coerceNumber()' 12.5 '成功,得到数字 12.5
coerceNumber()''失败
coerceNumber()true失败
coerceNumber()'Infinity'失败
coerceBoolean()'false'成功,得到 false
coerceBoolean()1 / 0成功,分别得到 true / false
coerceBoolean()'yes'失败

这些入口适合协议明确的基础类型转换。它们不自动表示“必填”或“必须大于 0”;null、undefined 等空值仍受内部规则的空值语义影响。

如果需要同时约束必填、整数和范围,可以用下一节的 preprocess():先处理输入,再交给数字规则校验。

3. 把数量转换和业务约束放在一起

假设订单数量必须是 1~99 的整数,输入可能来自 TextInput,因此先处理字符串:

import { v } from '@hmkit/validator';

const quantitySchema = v.preprocess<number>(
  (value: Object | null | undefined): Object | null | undefined => {
    if (typeof value !== 'string') {
      return value;
    }
    const text = value.trim();
    return text === '' ? undefined : Number(text);
  },
  v.number()
    .required('请输入数量')
    .integer('数量必须为整数')
    .min(1, '数量至少为 1')
    .max(99, '数量不能超过 99')
);

处理顺序是:

  1. 字符串先去除两侧空格。
  2. 空白输入转为缺失值,由 required() 返回必填提示。
  3. 其余字符串尝试转为数字。
  4. 数字继续经过整数和范围校验。

因此,'2' 得到数字 2,空白输入、'abc'、'1.5'、'0' 都无法通过这套完整规则。

这里的数量转换使用 Number()。如果接口只允许特定的十进制文本格式,可以在预处理函数中增加格式约束;金额计算和超大整数也应按业务精度要求单独设计。

4. 对象和数组可以继续组合

现在把数量规则放进订单。下面的代码接在上一节之后:

interface OrderItem {
  sku: string;
  quantity: number;
}

interface OrderData {
  phone: string;
  items: Array<OrderItem>;
}

const orderSchema = v.object<OrderData>({
  'phone': v.string()
    .required('请输入手机号')
    .phone('手机号格式不正确'),
  'items': v.array<OrderItem>(
    v.object<OrderItem>({
      'sku': v.string().required('请选择商品'),
      'quantity': quantitySchema
    }).strip()
  )
    .required('请添加商品')
    .nonEmpty('至少添加一件商品')
}).strip();

const rawInput: Record<string, Object> = {
  'phone': '13800138000',
  'items': [
    { 'sku': 'SKU-001', 'quantity': '2', 'selected': true }
  ],
  'previewMode': true
};

const parsed = orderSchema.parse(rawInput);

if (parsed.success && parsed.value !== undefined) {
  const order: OrderData = parsed.value;
  console.info(JSON.stringify(order));
  // 在这里把 order 交给提交请求等业务逻辑。
}

解析后的数据是:

{
  "phone": "13800138000",
  "items": [
    { "sku": "SKU-001", "quantity": 2 }
  ]
}

这个过程完成了三件事:校验手机号和商品结构,把数量从字符串转成数字,移除示例中的两个界面辅助字段。

其中,.strip() 只处理它所在的对象层级。因此订单对象和商品对象分别配置了 .strip()。

对象默认会保留未知字段。如果希望多出的字段直接报错,可以改用 .strict();如果希望接受这些字段,但不让它们进入解析结果,就使用 .strip()。

示例通过 v.object<OrderData>() 显式指定输出模型。业务接口和 Schema 的一致性仍需要开发者维护,泛型声明不会自动证明每个字段的规则都正确。

5. 出错时,定位到数组中的具体字段

商品列表中第二件商品的数量出错,只显示“表单填写有误”还不够,页面需要知道具体是哪一行。

@hmkit/validator 会把嵌套错误拼成字段路径:

import { flattenErrors } from '@hmkit/validator';

const invalidInput: Record<string, Object> = {
  'phone': '13800138000',
  'items': [
    { 'sku': 'SKU-001', 'quantity': '2' },
    { 'sku': 'SKU-002', 'quantity': '0' }
  ]
};

const failed = orderSchema.parse(invalidInput);

if (!failed.success) {
  const fieldErrors = flattenErrors(failed.errors);
  console.info(JSON.stringify(fieldErrors));
}

输出为:

{
  "items.1.quantity": ["数量至少为 1"]
}

items.1.quantity 表示商品数组中下标为 1,也就是第二件商品的数量字段。

业务层可以把这个路径映射到对应行的错误提示。原始错误还包含 message,内置规则提供的 code 可用于分类处理;flattenErrors() 则把同一路径的提示整理为数组,方便界面读取。

6. validate 和 parse 应该怎么选

需求使用入口读取内容
判断输入是否通过validate()valid、errors
获取转换、默认值或字段过滤后的数据parse()success、value、errors
规则包含远程异步校验validateAsync() / parseAsync()对应的异步结果

本文包含数量转换和未知字段过滤,所以提交时应使用成功解析后的 value。

如果只检查结果是否通过,随后仍提交 rawInput,数量依旧是字符串,界面辅助字段也仍然存在。校验结果和业务实际使用的数据,需要在这里接起来。

7. 这个库还适合哪些场景

除了本文的订单录入,它还可以用于注册资料、实名认证信息录入、企业资料表单和接口数据检查:

  • 中国本地化字段规则:手机号、身份证、银行卡、车牌、统一社会信用代码等,减少项目中重复维护校验逻辑的工作。格式和校验位检查仍需与业务侧的身份核验区分开。
  • ArkUI 表单联动:FormValidator 提供输入、失焦、提交等触发方式,以及字段错误和校验状态,适合需要交互反馈的页面。
  • 规则复用:对象 Schema 支持 partial()、pick()、omit();需要为不同场景构建独立可变规则时,1.4.0 还提供可选的 SchemaFactory 入口。

可以先挑一个表单接入,把字段规则、错误展示和提交数据串起来,再根据项目需要逐步使用其他能力。

开源地址与使用文档

@hmkit/validator 使用 MIT 开源协议。源码、示例和使用说明都在仓库中:

我会继续维护这个库。欢迎在自己的鸿蒙项目中试用,也欢迎带着具体的输入、预期结果和复现代码提 Issue,让后续改进更贴近实际使用场景。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐