一、什么是 Set?

Set 是 ES6 引入的有序列表集合,TS 完全继承并强化了它的类型特性,核心特点只有两个:

1.元素唯一性:自动去重,不会存储重复的值(包括基本类型和引用类型,规则不同)

2.无索引:和数组不同,Set 没有下标,不能通过 set[0] 取值

3.可迭代:支持 for...of、展开运算符等遍历方式

简单说:Set = 无重复值的集合,TS 给它加上了严格的类型校验,比 JS 原生 Set 更安全。

二、TypeScript 中 Set 的基础语法

(1)创建 Set(无初始值)

TS 中创建 Set 必须指定类型参数(泛型),这是和 JS 最大的区别,语法:

// 格式:new Set<类型>()
const emptySet = new Set<string>(); // 存储字符串类型的 Set
const numSet = new Set<number>();   // 存储数字类型的 Set
const boolSet = new Set<boolean>(); // 存储布尔类型的 Set

如果不指定类型,TS 会默认推断为 Set<unknown>,后续添加元素会报错,入门一定要养成指定类型的习惯

(2)创建 Set(带初始值)

可以直接传入数组初始化 Set,TS 会自动校验数组元素类型:

// 字符串 Set
const strSet = new Set<string>(["苹果", "香蕉", "橙子", "苹果"]); 
// 自动去重 → 最终值:{"苹果", "香蕉", "橙子"}

// 数字 Set
const numSet = new Set<number>([1, 2, 2, 3, 3, 3]); 
// 自动去重 → 最终值:{1,2,3}

// 混合类型(不推荐,除非业务需要)
const mixSet = new Set<string | number>([1, "TS", 2, "TS"]);

(3)重要:Set 的去重规则

基本类型(string/number/boolean/null/undefined/Symbol):值相等就去重

引用类型(对象 / 数组):引用地址相等才去重(直接写两个对象不会去重)

// 基本类型去重(生效)
const set1 = new Set([1, 1, true, true, null, null]);

// 引用类型去重(不生效,因为地址不同)
const set2 = new Set([{ name: "TS" }, { name: "TS" }]); 
console.log(set2.size); // 输出 2,两个对象地址不同

// 引用类型去重(生效,地址相同)
const obj = { name: "TS" };
const set3 = new Set([obj, obj]); 
console.log(set3.size); // 输出 1

三、Set 的核心属性

Set 只有一个核心属性,入门必记:

size:获取 Set 中元素的个数(类似数组的 length)

const set = new Set<number>([1,2,3]);
console.log(set.size); // 输出 3

四、Set 的核心方法(全)

TS 中 Set 的方法和 JS 完全一致,但自带类型校验,传错类型会直接报错,这是 TS 的优势。

(1)add(value):添加元素

向 Set 中添加新元素,重复值会自动忽略,返回 Set 本身(支持链式调用)。

const set = new Set<string>();
set.add("TS");
set.add("Set");
set.add("TS"); // 重复,不生效
console.log(set); // Set(2) {"TS", "Set"}

// 链式调用
set.add("Vue").add("React");

TS 校验:只能添加指定类型的元素,否则编译报错

set.add(123); // ❌ 报错:类型“number”不能赋值给类型“string”

(2)delete(value):删除元素

删除指定元素,返回布尔值(删除成功:true,元素不存在:false)。

const set = new Set<number>([1,2,3]);
set.delete(2); // true
set.delete(4); // false
console.log(set); // Set(2) {1,3}

(3)has(value):判断元素是否存在

检查 Set 中是否包含指定元素,返回布尔值(高频使用,比数组的 includes 性能更高)。

const set = new Set<string>(["TS", "Set"]);
console.log(set.has("TS")); // true
console.log(set.has("JS")); // false

(4)clear():清空所有元素

清空 Set 中的所有元素,无返回值。

const set = new Set<number>([1,2,3]);
set.clear();
console.log(set.size); // 0

(5)keys() / values() / entries():获取迭代器

Set 中键和值是同一个,所以 keys()values() 效果完全一致:

const set = new Set<string>(["TS", "Set"]);

// keys():返回键的迭代器
console.log(set.keys()); // SetIterator {"TS", "Set"}

// values():返回值的迭代器
console.log(set.values()); // SetIterator {"TS", "Set"}

// entries():返回 [键, 值] 迭代器(键=值)
console.log(set.entries()); // SetIterator {"TS" => "TS", "Set" => "Set"}

五、Set 的遍历方式(4 种常用)

Set 是可迭代对象,支持多种遍历方式,入门掌握前两种即可。

(1)for...of(最常用)

const set = new Set<number>([1,2,3]);
for (const item of set) {
  console.log(item); // 依次输出 1、2、3
}

(2)forEach()(和数组用法一致)

const set = new Set<string>(["TS", "Set", "入门"]);
set.forEach((item) => {
  console.log(item);
});

注意:Set 的 forEach 回调参数是 (value, key, set),因为无索引,key = value

(3)展开运算符 ...(快速转数组)

const set = new Set<number>([1,2,3]);
const arr = [...set]; // [1,2,3]

(4)迭代器遍历

const set = new Set<number>([1,2,3]);
const iterator = set.values();
console.log(iterator.next().value); // 1
console.log(iterator.next().value); // 2

六、TypeScript 中 Set 的高级类型用法

入门 TS,这几个类型场景一定要会,覆盖 90% 开发需求。

(1)存储对象类型(自定义接口)

Set 可以存储对象,必须用接口(interface) 约束类型,这是 TS 核心用法:

// 定义用户接口
interface User {
  id: number;
  name: string;
}

// 创建存储 User 类型的 Set
const userSet = new Set<User>([
  { id: 1, name: "张三" },
  { id: 2, name: "李四" },
]);

// 添加元素(必须符合 User 类型)
userSet.add({ id: 3, name: "王五" });

// 遍历对象 Set
userSet.forEach(user => {
  console.log(user.id, user.name);
});

(2)联合类型 Set

存储多种指定类型的元素:

// 存储 string 或 number 类型
const unionSet = new Set<string | number>(["TS", 123, "Set", 456]);

(3)只读 Set(ReadonlySet)

禁止修改的 Set,不能调用 add/delete/clear,适合常量集合:

const readOnlySet: ReadonlySet<number> = new Set([1,2,3]);
readOnlySet.add(4); // ❌ 报错:ReadonlySet 没有 add 方法

七、Set 高频实战场景(入门必练)

(1)数组去重(最简单)

const arr = [1,2,2,3,3,3,4];
const uniqueArr = [...new Set(arr)]; // [1,2,3,4]

(2)判断数组是否包含重复值

function hasDuplicate<T>(arr: T[]): boolean {
  return new Set(arr).size !== arr.length;
}

console.log(hasDuplicate([1,2,3])); // false
console.log(hasDuplicate([1,2,2])); // true

(3)集合运算:并集、交集、差集

const setA = new Set<number>([1,2,3]);
const setB = new Set<number>([2,3,4]);

// 并集(合并两个 Set,去重)
const union = new Set([...setA, ...setB]); // {1,2,3,4}

// 交集(两个 Set 都有的元素)
const intersection = new Set([...setA].filter(item => setB.has(item))); // {2,3}

// 差集(setA 有,setB 没有的元素)
const difference = new Set([...setA].filter(item => !setB.has(item))); // {1}

(4)字符串去重

const str = "typescript";
const uniqueStr = [...new Set(str)].join(""); // "typescrip"

八、Set vs WeakSet

WeakSet 是 Set 的弱引用版本

(1)Set

1.存储类型:任意类型(基本 + 引用)

2.引用方式:强引用(对象不会被垃圾回收)

3.核心方法:add/delete/has/size/clear

4.遍历:支持所有遍历方式

5.使用场景:通用集合存储

(2)WeakSet

1.存储类型:只能存储对象

2.引用方式:弱引用(对象无引用时自动回收)

3.核心方法:add/delete/has(无 size,无 clear)

4.遍历:不可遍历

5.使用场景:临时对象缓存、不影响垃圾回收

// WeakSet 用法(只能存对象)
const weakSet = new WeakSet();
const obj = { name: "TS" };
weakSet.add(obj);
console.log(weakSet.has(obj)); // true

九、Set 常见坑(入门避坑)

引用类型不自动去重:直接存两个相同对象不会去重,需要手动判断地址

不能通过索引取值set[0] 是 undefined,必须转数组或遍历

TS 类型必须一致:添加不同类型元素会直接报错

size 是属性不是方法:别写 set.size(),正确写法 set.size

更多推荐