You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为TypeScript组合式校验器配置类型实现编译时校验

TypeScript 类型守卫组合函数的类型约束实现

我定义了类型守卫函数的签名:

type Validator<T> = (doc: any) => doc is T;

现在需要实现union和intersection两个组合函数,要求配置参数validators的类型,让TypeScript编译器能自动校验:

  • 对于union<T>:传入的每个校验器对应的类型必须是T的子类型,组合后能正确守卫T
  • 对于intersection<T>:传入的所有校验器对应的类型的交叉必须等于T,不能少传必要的校验器

期望的校验效果如下:

interface FooOne {
  a: string;
}

interface FooTwo {
  b: string;
}

interface FooThree {
  c: string
}

type Bar = FooOne | FooTwo
type Baz = FooOne & FooTwo

// 基础校验器生成函数示例
const validator = <T>(): Validator<T> => (doc: any): doc is T => true;

const oneValidator = validator<FooOne>()
const twoValidator = validator<FooTwo>()
const threeValidator = validator<FooThree>()


const barValidator = union<Bar>([oneValidator, twoValidator])  // 正常通过
const barValidator = union<Bar>([oneValidator])  // 正常通过,FooOne是Bar的子类型
const barValidator = union<Bar>([oneValidator, twoValidator, threeValidator])  // 报错,FooThree不属于Bar

const bazValidator = intersection<Baz>([oneValidator, twoValidator]) // 正常通过
const bazValidator = intersection<Baz>([oneValidator]) // 报错,仅FooOne无法覆盖Baz的全部属性
const bazValidator = intersection<Baz>([oneValidator, twoValidator, threeValidator]) // 报错,FooThree不属于Baz的组成部分

1. 实现union组合函数的类型约束

核心逻辑是确保每个传入的校验器对应的类型U是T的子类型,这样所有校验器的联合就能覆盖T的范围:

function union<T, U extends T>(validators: Validator<U>[]): Validator<T> {
  return (doc: any): doc is T => {
    return validators.some(validator => validator(doc));
  };
}

逻辑说明

  • 泛型U extends T约束了数组中所有校验器的目标类型必须是T的子类型
  • 只要有一个校验器通过,就认为当前值属于T类型,符合联合类型的判断逻辑

2. 实现intersection组合函数的类型约束

核心是确保传入的所有校验器类型的交叉结果等于T,同时每个类型都是T的子类型:

// 辅助类型:将数组中所有校验器的类型交叉合并
type CombineValidators<T extends Validator<any>[]> = 
  T extends [Validator<infer U>, ...infer Rest]
    ? Rest extends Validator<any>[]
      ? U & CombineValidators<Rest>
      : U
    : unknown;

function intersection<T>(
  validators: CombineValidators<typeof validators> extends T ? Validator<any>[] : never
): Validator<T> {
  return (doc: any): doc is T => {
    return validators.every(validator => validator(doc));
  };
}

逻辑说明

  • CombineValidators类型会递归提取数组中每个校验器的目标类型,并将它们交叉合并
  • 通过条件类型判断合并后的交叉类型是否等于T,不满足则参数类型变为never,触发编译错误
  • 只有所有校验器都通过,才认为当前值属于T类型,符合交叉类型的判断逻辑

验证效果

使用上述实现后:

  • 传入不符合T范围的校验器(如给union<Bar>传threeValidator)会直接报错
  • 给intersection<Baz>只传oneValidator时,合并后的交叉类型是FooOne,不等于Baz,触发报错
  • 符合预期的调用会正常通过编译

内容的提问来源于stack exchange,提问作者theahura

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 07:25:38