返回首页

PropertyDescriptors 用于 TypeScript 模式验证

验证库使用 PropertyDescriptors 进行类型安全的错误选择器,而非字符串路径。支持不可变 fluent API、运行时内省和扩展。与 Standard Schema v1 完全兼容,适用于生态系统。

类型安全验证:使用 PropertyDescriptors 而非字符串
Advertisement 728x90

TypeScript 属性描述符:模式验证的类型安全错误处理

在 JavaScript 项目中,开发者常常为验证复杂嵌套对象而烦恼。传统方法使用字符串路径提取错误,容易在重构时引发运行时 bug。解决方案?属性描述符:一个结构化的属性描述符树,提供类型安全的选择器,并支持完整的 IDE 自动补全。

核心思路是,每个模式构建一个节点树,每个描述符都知道自己的属性路径、关联模式、父级以及访问方法。这样,你可以用 Lambda 表达式编译选择器,而不是脆弱的字符串。

import { object, string, number, InferType } from '@cleverbrush/schema';

const OrderSchema = object({
    id: string(),
    customer: object({
        name: string().minLength(2),
        email: string().email(),
        address: object({
            city: string().required('City is required'),
            zip: number()
        })
    }),
    total: number().min(0)
});

const result = OrderSchema.validate({
    id: '123',
    customer: {
        name: 'A',
        email: 'not-an-email',
        address: {
            zip: -1
        }
    },
    total: -5
});

const nameErrors = result.getErrorsFor(t => t.customer.name);
const cityErrors = result.getErrorsFor(t => t.customer.address.city);

重命名字段,TypeScript 会在编译时捕获错误。IDE 提供完整的自动补全。

Google AdInline article slot

不可变流式 API 和构建器

所有模式方法调用都返回新实例,避免突变。这让组合和复用变得轻而易举。

库提供了 14 种构建器类型:

  • string() — minLength、maxLength、matches、email、url、uuid
  • number() — min、max、positive、negative、finite、multipleOf
  • boolean() — 基础验证
  • date() — minDate、maxDate
  • object() — 命名属性
  • array() — 类型化元素、非空
  • union() — 判别联合
  • tuple() — 固定长度
  • record() — 动态键
  • func() — 函数类型
  • any() — 带 hasType
  • nul() — null
  • lazy() — 递归模式
  • extern() — Standard Schema v1 包装器

基础 SchemaBuilder 的常用方法:

Google AdInline article slot
  • .optional() — 接受 undefined
  • .required('msg') — 必填字段
  • .nullable() — 接受 null
  • .default(value) — 默认值
  • .catch(value) — 错误回退
  • .readonly() — Readonly<T>
  • .brand<'T'>() — 品牌类型
  • .describe('text') — 保留 JSDoc
  • .addValidator(fn) — 自定义逻辑
  • .addPreprocessor(fn) — 预处理
  • .introspect() — 元数据

运行时模式自省

.introspect() 方法返回完整的模式描述:类型、约束、标志、描述、扩展。运行时全部可用,适用于工具和集成。

const Name = string().minLength(2).maxLength(100).describe('User name').optional();

const info = Name.introspect();
// info.type === 'string'
// info.minLength === 2
// info.description === 'User name'

自省功能针对每个构建器完全展开。完美适用于 JSON Schema 转换、表单生成或自定义渲染器。

类型化扩展系统

扩展是可组合、可自省的核心插件。例如 HEX 颜色和端口:

Google AdInline article slot
const hexColorExt = defineExtension({
    string: {
        hexColor(this: StringSchemaBuilder) {
            return this.addValidator((v) => {
                const valid = /^#[0-9a-f]{6}$/i.test(v as string);
                return {
                    valid,
                    errors: valid ? [] : [{ message: 'Must be a valid HEX color' }]
                };
            });
        }
    }
});

const s = withExtensions(hexColorExt, portExt);
const colorSchema = s.string().hexColor();

扩展出现在 .introspect().extensions 中。内置包括 email、url、uuid、positive、非空。

Standard Schema v1 兼容性

模式实现了 Standard Schema v1,与 tRPC、TanStack Form、React Hook Form、Hono 无缝集成。extern() 将 Zod/Valibot 包装成构建器。

const UserSchema = object({
    name: string().minLength(2),
    address: extern(zodAddress)
});

属性描述符即使在混合模式中,也提供类型安全的错误访问。

核心要点

  • 类型安全选择器:通过属性描述符消除字符串路径和运行时 bug。
  • 不可变流式 API:简化无副作用组合。
  • 运行时自省:为任意工具提取元数据。
  • 扩展:类型化、可组合的核心插件。
  • Standard Schema v1:确保生态兼容性。

— Editorial Team

Advertisement 728x90

继续阅读