es-toolkit 的 isSet:基于 instanceof 的 Set 类型守卫与 Lodash 兼容实现解析
2026/9/15 18:34:42 网站建设 项目流程

es-toolkit 的 isSet:基于 instanceof 的 Set 类型守卫与 Lodash 兼容实现解析

【免费下载链接】es-toolkitA modern JavaScript utility library that's 2-3 times faster and up to 97% smaller, a major upgrade to lodash.项目地址: https://gitcode.com/GitHub_Trending/es/es-toolkit

isSet是 es-toolkit 提供的一个轻量级类型判断函数,用于检测一个值是否为 JavaScript 内建的Set实例。它同时提供主库版本(es-toolkit/predicate)与 Lodash 兼容版本(es-toolkit/compat),两者实现完全一致,并可作为 TypeScript 类型守卫使用。读完本文,你将掌握isSet的 API 签名、与Map/WeakSet/数组/普通对象的区分行为、在集合运算与数据转换中的实战用法,以及它在 es-toolkit 源码内部的实现原理与调用关系。

函数概览:一个instanceof的封装

isSet的核心实现极简,整个函数体只有一行,基于 JavaScript 内建的instanceof运算符完成判断。主库实现位于 src/predicate/isSet.ts:

export function isSet(value: unknown): value is Set<any> { return value instanceof Set; }

关键点在于返回值类型被声明为value is Set<any>,这是一个 TypeScript 类型谓词(type predicate)。当isSet(value)返回true时,TypeScript 编译器会在后续代码中自动将value的类型收窄为Set<any>,从而允许你直接访问sizehasadd等 Set 专有成员而无需手动类型断言。

Lodash 兼容版本位于 src/compat/predicate/isSet.ts,它没有重复实现逻辑,而是直接委托给主库版本:

import { isSet as isSetToolkit } from '../../predicate/isSet.ts'; export function isSet(value?: any): value is Set<any> { return isSetToolkit(value); }

两者唯一的差别是兼容版参数声明为value?: any(可选参数),这与 Lodash 允许"无参调用返回 false"的行为保持一致——这一点在兼容版的测试用例中有明确体现(见下文测试分析)。

API 说明

签名

function isSet(value: unknown): value is Set<any>
参数
  • valueunknown):需要判断是否为Set的值。
返回值

value is Set<any>):若valueSet实例,返回true,否则返回false

基本用法

es-toolkit/compat导入

import { isSet } from 'es-toolkit/compat'; // Set 判断 const set = new Set(); isSet(set); // true // 其他类型一律返回 false isSet(new Map()); // false isSet(new WeakSet()); // false isSet([]); // false isSet({}); // false isSet('set'); // false isSet(123); // false isSet(null); // false isSet(undefined); // false

与相似集合类型的区分

SetMapWeakSet在形态上相似,容易混淆,isSet可以精确区分它们:

import { isSet } from 'es-toolkit/compat'; // Set vs Map vs WeakSet isSet(new Set([1, 2, 3])); // true isSet(new Map([['key', 'value']])); // false isSet(new WeakSet()); // false // Set vs 数组 isSet(new Set([1, 2, 3])); // true isSet([1, 2, 3]); // false // Set vs 普通对象 isSet(new Set()); // true isSet({}); // false isSet(Object.create(null)); // false

由于底层是instanceof Set,任何非 Set 内建实例(包括Object.create(null)创建的无原型对象)都会得到false

实战场景:类型守卫驱动集合运算

主库文档 docs/reference/predicate/isSet.md 展示了isSet作为类型守卫在真实逻辑中的价值。最典型的场景是编写接受"未知类型"的通用集合处理函数,让 TypeScript 自动完成类型收窄。

计算集合大小

function getCollectionSize(collection: unknown): number { if (isSet(collection)) { // TypeScript 在此处将 collection 推断为 Set<any> return collection.size; } if (Array.isArray(collection)) { return collection.length; } if (collection && typeof collection === 'object') { return Object.keys(collection).length; } return 0; } // 使用示例 getCollectionSize(new Set([1, 2, 3])); // 3 getCollectionSize([1, 2, 3]); // 3 getCollectionSize({ a: 1, b: 2 }); // 2

如果没有类型谓词,collection.size一行就会因为collection仍是unknown而无法通过编译;isSetvalue is Set<any>返回值让这段代码既安全又简洁。

去重工具

function removeDuplicates(data: unknown) { if (isSet(data)) { // 本身就是 Set,原样返回 return data; } if (Array.isArray(data)) { return new Set(data); } // 其他类型不转换 return data; } const duplicatedArray = [1, 2, 2, 3, 3, 3]; const uniqueSet = removeDuplicates(duplicatedArray); console.log(uniqueSet); // Set { 1, 2, 3 } const existingSet = new Set(['a', 'b']); console.log(removeDuplicates(existingSet)); // Set { 'a', 'b' }(返回同一个 Set)

通用集合合并与交集计算

isSet还可以用于编写同时接受Set与数组的混合集合运算函数:

// 通用集合合并 function mergeCollections(...collections: unknown[]): Set<any> { const result = new Set(); for (const collection of collections) { if (isSet(collection)) { // 将 Set 中的所有值加入 result for (const item of collection) { result.add(item); } } else if (Array.isArray(collection)) { // 将数组中的所有值加入 result for (const item of collection) { result.add(item); } } } return result; } const set1 = new Set([1, 2, 3]); const array1 = [3, 4, 5]; const set2 = new Set(['a', 'b']); const merged = mergeCollections(set1, array1, set2); console.log(merged); // Set { 1, 2, 3, 4, 5, 'a', 'b' } // 计算集合交集 function getIntersection(coll1: unknown, coll2: unknown): Set<any> { const set1 = isSet(coll1) ? coll1 : new Set(Array.isArray(coll1) ? coll1 : []); const set2 = isSet(coll2) ? coll2 : new Set(Array.isArray(coll2) ? coll2 : []); const intersection = new Set(); for (const item of set1) { if (set2.has(item)) { intersection.add(item); } } return intersection; } const setA = new Set([1, 2, 3, 4]); const arrayB = [3, 4, 5, 6]; const intersection = getIntersection(setA, arrayB); console.log(intersection); // Set { 3, 4 }

源码实现与调用链分析

主库与兼容版的导出路径

  • 主库版本通过 src/predicate/index.ts 中的export { isSet } from './isSet.ts'对外导出,可通过es-toolkit/predicate子路径或es-toolkit主入口引入。
  • 兼容版本通过 src/compat/compat.ts 中的export { isSet } from './predicate/isSet.ts'对外导出,可通过es-toolkit/compat引入。

从源码结构看,兼容版的设计遵循"薄封装"原则:所有判读逻辑只实现一次(主库),兼容层仅负责适配 Lodash 风格的调用习惯,避免逻辑重复与潜在的实现漂移。

内部的真实调用方

isSet不仅是面向用户公开的 API,也是 es-toolkit 内部其他工具函数的依赖,这印证了它作为基础谓词的地位:

  • src/compat/util/toArray.ts 在将类数组、MapSet转换为数组时调用了isSet,用于决定是否走Array.from(value)的转换路径(判断条件为isArrayLike(value) || isMap(value) || isSet(value))。
  • src/util/serialize/serializeObject.ts 在序列化对象时调用isSet,对Set类型进行专门的序列化处理。

WeakSet等边界情况的处理

isSet使用instanceof Set判断,而WeakSetSet是不同的内建类,二者不构成原型链关系,因此new WeakSet()会正确返回false。这一边界行为在测试用例中得到了系统性的覆盖。

测试验证:行为契约的完整覆盖

es-toolkit 为主库与兼容版分别维护了测试文件,共同构成isSet的行为契约。

主库测试 src/predicate/isSet.spec.ts 覆盖两类断言:

  1. new Set()返回true
  2. null''123{}[]new Map()new WeakSet()等非 Set 值全部返回false

兼容版测试 src/compat/predicate/isSet.spec.ts 覆盖面更广,重点验证与 Lodash 的行为对齐:

  • 使用falsey(falsy 值集合,来自 src/compat/_internal/falsey.ts)逐一断言0''NaNnullundefined等 falsy 值均返回false
  • 通过isSet()无参调用验证兼容版的可选参数行为(value?: any),返回false
  • arguments对象、数组、布尔值、DateError、函数(slice)、普通对象、数字、正则、字符串、SymbolWeakSet等逐一断言返回false
  • 额外覆盖了"具有非函数constructor属性的对象"这一 Lodash 时代遗留的 IE 11 兼容场景,验证其返回false(对应 src/compat/_internal/weakSet.ts 中导出的weakSet实例测试)。

从这些测试可以看出,isSet的判定是严格且无歧义的:只有真正的Set实例才返回true

主库版本 vs 兼容版本:如何选择

isSet在 es-toolkit 中有两个入口,使用建议如下:

入口导入方式适用场景
主库版本import { isSet } from 'es-toolkit/predicate'(或es-toolkit主入口)新项目、现代代码风格,推荐使用
兼容版本import { isSet } from 'es-toolkit/compat'从 Lodash 迁移、需要保持既有调用习惯的项目

兼容版文档 docs/compat/reference/predicate/isSet.md 明确建议:该兼容函数与主库实现相同,应优先使用更现代的es-toolkit主库版本 isSet。两者的运行时行为一致,差异仅在于兼容版为对齐 Lodash 而允许无参调用。如果你的代码库不需要 Lodash 兼容语义,直接使用主库版本即可获得同样正确、体积更优的类型守卫。

【免费下载链接】es-toolkitA modern JavaScript utility library that's 2-3 times faster and up to 97% smaller, a major upgrade to lodash.项目地址: https://gitcode.com/GitHub_Trending/es/es-toolkit

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询