es-toolkit/compat `overEvery` 源码级解析:多条件复合校验函数的实现与使用

📅 发布时间:2026/9/16 11:01:20
es-toolkit/compat `overEvery` 源码级解析:多条件复合校验函数的实现与使用
es-toolkit/compatoverEvery源码级解析多条件复合校验函数的实现与使用【免费下载链接】es-toolkitA modern JavaScript utility library thats 2-3 times faster and up to 97% smaller, a major upgrade to lodash.项目地址: https://gitcode.com/GitHub_Trending/es/es-toolkit导读overEvery是 es-toolkit 兼容层es-toolkit/compat中用于「多条件复合校验」的工具函数它接收多个谓词predicate返回一个新函数只有当下游传入的值同时满足全部条件时才返回true。本文以 overEvery 官方文档 为主线结合 overEvery 源码、iteratee 源码 与 overEvery 测试用例讲解它的四种简写形式函数、属性名、对象、属性-值对、短路求值机制、类型重载以及它与原生Array.every的取舍帮助你在从 Lodash 迁移或日常数据验证场景中正确、高效地使用它。一、背景overEvery在 compat 兼容层中的定位es-toolkit 提供了两套 API面向新项目的类型安全主库es-toolkit以及 1:1 复刻 Lodash 行为的es-toolkit/compat兼容层。overEvery属于后者其语义与 Lodash 的_.overEvery一致创建一个函数检查传入的值是否让所有谓词都返回真值truthy。从源码结构看它被同时导出在两个入口中compat 统一入口export { overEvery } from ./util/overEvery.ts;browser 入口export { overEvery } from ./compat/util/overEvery.ts;同时与es-toolkit/compat的所有函数一样它也支持按需单独导入详见 compat 介绍文档在无 tree-shaking 的环境如 CommonJSrequire、React Native下可以显著减小加载体积import { overEvery } from es-toolkit/compat; // 或按需导入 import overEvery from es-toolkit/compat/overEvery;二、核心 API 与基础用法函数签名const allValidator overEvery(predicates);参数...predicatesArrayFunction | string | object | Array要检查的谓词集合。可以是函数、属性名string、对象partial object或属性-值对[key, value]数组。返回值(...args: any[]) boolean返回一个新函数当所有条件都满足时返回true只要有一个条件不满足就返回false。overEvery与 Lodash 的_.overSome任一条件满足即通过是对偶关系适合复合条件判断与数据校验场景。用函数谓词做复合校验import { overEvery } from es-toolkit/compat; // 检查字符串条件 const isValidString overEvery([ value typeof value string, value value.length 3, value value.includes(o), ]); isValidString(hello); // true isValidString(hi); // false (长度小于等于 3) isValidString(test); // false (不含 o) // 检查数字范围 const isInRange overEvery([ num num 0, num num 100, num num % 1 0, // 检查是否为整数 ]); isInRange(50); // true isInRange(-5); // false (小于 0) isInRange(150); // false (大于 100) isInRange(50.5); // false (不是整数)用简写谓词检查对象属性除函数外overEvery还支持三种 Lodash 风格的简写import { overEvery } from es-toolkit/compat; // 检查对象属性 const isValidUser overEvery([ name, // 属性名name 属性是否为真值 { age: 30 }, // 部分对象age 是否等于 30 [active, true] // 属性-值对active 是否为 true ]); isValidUser({ name: John, age: 30, active: true }); // true isValidUser({ name: , age: 30, active: true }); // false (name 是空字符串) isValidUser({ name: John, age: 25, active: true }); // false (age 不同)三、源码实现简写如何被统一为函数overEvery之所以能同时接受函数、字符串、对象和数组四种谓词关键在于它对每个谓词调用了 compat 层的iteratee转换器见 iteratee.ts。其转换规则为函数原样返回直接作为谓词使用null/undefined转换为恒等函数identity即把传入值本身当作真值判断长度为 2 的数组属性-值对转换为matchesProperty谓词检查对象在指定属性路径上的值是否与给定值匹配普通对象转换为matches谓词检查对象是否部分匹配其他字符串、数字等 PropertyKey转换为property谓词取出对象上对应属性并判断其真值性。matchesProperty的具体实现位于 matchesProperty.ts它会通过get按属性路径取值若取到undefined则回退用has判断属性是否存在并对源值做cloneDeep拷贝以避免外部修改影响后续比较。这意味着简写形式并不只是「语法糖」而是完整复刻了 Lodash 的深层匹配语义。overEvery的主体实现overEvery.ts非常精简export function overEveryT( ...predicates: Array((...values: T[]) boolean) | ReadonlyArray(...values: T[]) boolean ): (...values: T[]) boolean { return function (this: any, ...values: T[]) { for (let i 0; i predicates.length; i) { const predicate predicates[i]; if (!Array.isArray(predicate)) { if (!createIteratee(predicate).apply(this, values)) { return false; } continue; } for (let j 0; j predicate.length; j) { if (!createIteratee(predicate[j]).apply(this, values)) { return false; } } } return true; }; }从这段实现可以提炼出三个关键行为1. 短路求值short-circuit循环中一旦某个谓词返回假值函数立即return false不再继续执行剩余谓词。overEvery 测试 用计数函数验证了这一点overEvery(countTrue, countFalse, countTrue)调用后计数为 2说明第三个countTrue从未被执行。这在谓词包含昂贵计算时能显著节省开销。2. 数组谓词的扁平化flatteningoverEvery(stubTrue, [stubFalse])这种「单个函数 数组」的混写形式是合法的外层循环遇到数组时进入内层循环逐一展开执行。测试 overEvery.spec.ts 专门覆盖了这一行为。也正因如此[active, true]这样长度为 2 的数组简写需要放在数组内部如overEvery([[active, true]])才能与「谓词列表」语义区分开。3.this绑定透传返回函数内部通过predicate.apply(this, values)调用谓词把外层函数调用时的this透传给所有谓词。测试 overEvery.spec.ts 演示了这一点将overEvery的结果挂到对象上作为方法调用时谓词内的this指向该对象从而可以访问this.a、this.b。同时...values会把所有实参原样传递给每个谓词测试 overEvery.spec.ts 确认over(a, b, c)时谓词收到完整的[a, b, c]参数列表。四、类型系统类型守卫与重载overEvery的类型定义overEvery.ts提供了两个重载// 重载 1两个谓词时启用类型守卫返回值收窄为交集类型 export function overEveryT, U extends T, V extends T( predicate1: (value: T) value is U, predicate2: (value: T) value is V ): (value: T) value is U V; // 重载 2通用形式 export function overEveryT( ...predicates: Array((...values: T[]) boolean) | ReadonlyArray(...values: T[]) boolean ): (...values: T[]) boolean;第一个重载是值得关注的类型细节当恰好传入两个带类型守卫type guard的谓词时返回函数本身也会被推断为类型守卫(value: T) value is U V。这意味着你可以把overEvery的结果直接当作类型守卫用在if分支或数组filter中编译器会自动把变量收窄为U Vtype Pet { name: string; legs: number }; const isDog (p: Pet): p is Dog p.legs 4; const isNamed (p: Pet): p is NamedDog p.name.length 0; const isValidPet overEvery(isDog, isNamed); // 推断类型(value: Pet) value is Dog NamedDog五、测试验证与行为边界overEvery.spec.ts 共覆盖 9 组行为除了上文提到的短路求值、扁平化、参数透传、this绑定外还包括nullish 谓词退化为 identityoverEvery(undefined, null)等价于直接判断传入值的真值性over(true)为true、over(false)为false见 测试 L32-L38property简写overEvery(b, a)检查对象上b、a两个属性是否都为真值L40-L46matches简写overEvery({ b: 2 }, { a: 1 })检查对象是否同时部分匹配两个条件L48-L54matchesProperty简写与歧义区分overEvery([a, 1])中[a, 1]被当作「函数数组」展开后a是属性简写、1是数字简写取属性1的真值而overEvery([[a, 1]])中内层二元数组才被识别为「属性-值对」L56-L81。这一测试清晰地划定了两种数组语义的边界。六、性能提示与替代方案原文档在开头给出了明确警告overEvery在转换和检查谓词的过程中会产生额外开销官方建议优先使用更快、更现代的Array.every// 用原生 every 替代 overEvery const isValidString (value: unknown) [ (v: unknown) typeof v string, (v: string) v.length 3, (v: string) v.includes(o), ].every(fn fn(value));从实现看开销来源有二一是每个谓词即使是纯函数每次调用都要经过createIteratee转换判断函数类型分支除外见 iteratee.ts二是返回函数通过apply动态调用并展开arguments。因此overEvery的价值主要体现在与 Lodash 代码库的 1:1 兼容上——迁移阶段不改调用点即可工作迁移流程详见 compat 介绍而在可以自由改写的新代码中直接用Array.every或手写组合条件通常是更优选择。七、小结overEvery(...predicates)创建「全条件通过」校验器支持函数、属性名、部分对象、属性-值对四种谓词简写由 iteratee.ts 统一转换具备短路求值遇假即停、数组谓词扁平化与this/多参数透传三个实现特性均有测试用例佐证恰好两个类型守卫谓词时返回值被推断为value is U V类型守卫可直接用于类型收窄作为 Lodash 兼容 API它优先服务于存量代码迁移新代码请按官方建议优先使用Array.every。【免费下载链接】es-toolkitA modern JavaScript utility library thats 2-3 times faster and up to 97% smaller, a major upgrade to lodash.项目地址: https://gitcode.com/GitHub_Trending/es/es-toolkit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考