📚 TypeScript 教程系列
入门与配置 基础类型与变量声明 函数 流程控制与运算符 集合类型 (本文)异步编程与错误处理 接口与类 泛型与类型组合 高级类型 模块、装饰器与工程化 TypeScript 与 Bun 实战 TypeScript 与 React 实战 ⚠️ 来源声明 :本文内容参考自 菜鸟教程 TypeScript 教程 ,仅供学习交流,版权归原作者所有。 TypeScript 在 JavaScript 的基础上为各类集合类型加上了静态类型约束,使得数组、元组、枚举、Map、Set 以及对象在编写阶段就能获得类型检查与智能提示。本篇将这几种常用的集合类型串联起来,从声明、访问到常用方法逐一展开,帮助你建立完整的类型化数据结构心智模型。
数组(Array) 数组对象用于使用单独的变量名来存储一系列的值。例如,将多个网站名字各自存入单独变量会非常繁琐,而使用数组则简洁得多:
1 2 let sites :string []; sites = ["Google" ,"Runoob" ,"Taobao" ]
声明数组 TypeScript 声明数组的语法格式如下:
1 2 let array_name[:datatype]; array_name = [val1,val2,valn..]
或者直接在声明时初始化:
1 let array_name[:datatype] = [val1,val2…valn]
若声明数组时未指定类型但提供了初始值,TypeScript 会根据初始值推断数组类型(如 let arr = [1, 2, 3] 推断为 number[])。在 strict 模式(tsc --init 默认开启,含 noImplicitAny)下,未指定类型又无初始化值(如 let arr;)会报隐式 any 错误,需显式标注类型。
创建一个 number 类型的数组:
1 const numlist :number [] = [2 ,4 ,6 ,8 ]
索引值从 0 开始,可通过索引访问元素:
1 2 3 4 let sites :string []; sites = ["Google" ,"Runoob" ,"Taobao" ]console .log (sites[0 ]);console .log (sites[1 ]);
输出:
声明时直接初始化的示例:
1 2 3 4 5 const nums :number [] = [1 ,2 ,3 ,4 ]console .log (nums[0 ]);console .log (nums[1 ]);console .log (nums[2 ]);console .log (nums[3 ]);
输出:
Array 对象 也可以使用 Array 对象来创建数组。其构造函数接受以下两种值:
实例:指定数组初始化大小:
1 2 3 4 5 const arr_names :number [] = new Array (4 )for (let i = 0 ; i<arr_names.length ; i++) { arr_names[i] = i * 2 console .log (arr_names[i]) }
⚠️ new Array(size) 会创建含 empty 槽位的稀疏数组,是已知 footgun。现代写法推荐 Array.from({ length: 4 }, (_, i) => i * 2),或先 new Array(4).fill(0) 填充再赋值;优先使用字面量 []。
输出:
实例:直接初始化数组元素:
1 2 3 4 const sites :string [] = new Array ("Google" ,"Runoob" ,"Taobao" ,"Facebook" )for (let i = 0 ;i<sites.length ;i++) { console .log (sites[i]) }
输出:
1 2 3 4 Google Runoob Taobao Facebook
数组解构 可将数组元素赋值给变量:
1 2 3 4 const arr :number [] = [12 ,13 ]let [x,y] = arr console .log (x)console .log (y)
输出:
数组迭代 使用 for...of 语句循环输出数组元素(for...in 会以字符串形式返回索引并可能枚举原型属性,不推荐用于数组):
1 2 3 4 const nums : number [] = [1001 , 1002 , 1003 , 1004 ];for (const num of nums) { console .log (num); }
输出:
展开运算符(Spread) 展开运算符 ... 可以把数组、对象或可迭代对象「展开」为独立元素,常用于拷贝、合并与函数调用传参。它与剩余参数 (rest)共用 ... 语法,但方向相反:剩余参数是「收集」,展开是「拆开」。
数组展开 :复制数组、合并数组。
1 2 3 4 5 6 const a = [1 , 2 , 3 ];const b = [4 , 5 ];const copy = [...a]; const merged = [...a, ...b]; const withExtra = [0 , ...a, 6 ];
对象展开 :浅拷贝对象、合并属性(后展开的同名属性会覆盖前者)。
1 2 3 4 const user = { name : "Alice" , age : 30 };const copy = { ...user }; const updated = { ...user, age : 31 }; const withRole = { ...user, role : "admin" };
函数调用展开 :把数组元素作为独立参数传入函数。
1 2 3 4 5 function sum (a : number , b : number , c : number ): number { return a + b + c; }const nums : [number , number , number ] = [1 , 2 , 3 ];console .log (sum (...nums));
⚠️ 展开运算符做的是浅拷贝 :嵌套的数组或对象内层仍是引用,修改副本的内层会影响原对象。需要深拷贝时请使用 structuredClone() 或递归处理。
多维数组 一个数组的元素可以是另外一个数组,这样就构成了多维数组。最简单的二维数组定义方式:
1 let arr_name :datatype[][]=[ [val1,val2,val3],[v1,v2,v3] ]
实例:
1 2 3 4 5 6 7 const multi :number [][] = [[1 ,2 ,3 ],[23 ,24 ,25 ]]console .log (multi[0 ][0 ])console .log (multi[0 ][1 ])console .log (multi[0 ][2 ])console .log (multi[1 ][0 ])console .log (multi[1 ][1 ])console .log (multi[1 ][2 ])
输出:
数组在函数中的使用 作为参数传递给函数:
1 2 3 4 5 6 7 const sites :string [] = new Array ("Google" ,"Runoob" ,"Taobao" ,"Facebook" )function disp (arr_sites :string [] ) { for (let i = 0 ; i < arr_sites.length ; i++) { console .log (arr_sites[i]) } }disp (sites);
输出:
1 2 3 4 Google Runoob Taobao Facebook
作为函数的返回值:
1 2 3 4 5 6 7 function disp ( ):string [] { return new Array ("Google" , "Runoob" , "Taobao" , "Facebook" ); }const sites : string [] = disp ();for (const site of sites) { console .log (site); }
输出:
1 2 3 4 Google Runoob Taobao Facebook
数组方法 下表列出了一些常用的数组方法:
方法 & 描述 实例 concat() 连接两个或更多的数组,返回结果const alpha = ["a", "b", "c"];const numeric = [1, 2, 3];const alphaNumeric = alpha.concat(numeric); → a,b,c,1,2,3every() 检测每个元素是否都符合条件function isBigEnough(element) {return element >= 10;}const passed = [12, 5, 8, 130, 44].every(isBigEnough); → falsefilter() 返回符合条件所有元素的数组const passed = [12, 5, 8, 130, 44].filter(isBigEnough); → 12,130,44forEach() 每个元素执行一次回调函数let num = [7, 8, 9];num.forEach(function (value) {console.log(value);}); → 7 8 9indexOf() 搜索元素并返回位置,未找到返回 -1const index = [12, 5, 8, 130, 44].indexOf(8); → 2join() 将所有元素放入一个字符串const arr = new Array("Google","Runoob","Taobao");const str = arr.join(); → Google,Runoob,Taobaoarr.join(" + ") → Google + Runoob + TaobaolastIndexOf() 返回指定值最后出现的位置,从后向前搜索const index = [12, 5, 8, 130, 44].lastIndexOf(8); → 2map() 通过指定函数处理每个元素,返回处理后的数组const numbers = [1, 4, 9];const roots = numbers.map(Math.sqrt); → 1,2,3pop() 删除最后一个元素并返回它const numbers = [1, 4, 9];const element = numbers.pop(); → 9push() 向末尾添加一个或多个元素,返回新长度const length = numbers.push(10); → 1,4,9,10reduce() 将数组元素计算为一个值(从左到右)const total = [0, 1, 2, 3].reduce(function(a, b) {return a + b;}); → 6reduceRight() 将数组元素计算为一个值(从右到左)const total = [0, 1, 2, 3].reduceRight(function(a, b) {return a + b;}); → 6reverse() 反转数组元素顺序const arr = [0, 1, 2, 3].reverse(); → 3,2,1,0shift() 删除并返回第一个元素const arr = [10, 1, 2, 3].shift(); → 10slice() 选取一部分并返回新数组const arr = ["orange", "mango", "banana", "sugar", "tea"];arr.slice(1, 2); → mangosome() 检测是否有元素符合条件const retval = [2, 5, 8, 1, 4].some(isBigEnough); → false 改为 [12, 5, 8, 1, 4] → truesort() 对元素排序const sorted = arr.sort(); → banana,mango,orange,sugarsplice() 从数组中添加或删除元素const removed = arr.splice(2, 0, "water"); 插入 watertoString() 将数组转换为字符串const str = arr.toString(); → orange,mango,banana,sugarunshift() 向开头添加一个或多个元素,返回新长度const length = arr.unshift("water"); → water,orange,mango,banana,sugar
💡 TS 5.5+ 类型谓词推断 :当 filter 回调返回布尔表达式或类型谓词时,TypeScript 能自动推断结果元素类型。例如 .filter(x => x != null) 无需手写 (x): x is T 即可从 (T | null)[] 收窄为 T[]。
元组(Tuple) 我们知道数组中元素的数据类型一般相同(any[] 类型的数组可以不同),如果存储的元素数据类型不同,则需要使用元组。元组是一种特殊数组,允许存储不同类型的元素,每个元素都有明确的类型和位置,适合表示固定长度且各元素类型已知的数据结构。
创建元组 语法格式:
1 let tuple : [类型1 , 类型2 , 类型3 , ...];
实例:
1 2 let mytuple : [number , string ]; mytuple = [42 , "Runoob" ];
访问元组 通过索引访问,索引从 0 开始,语法:tuple_name[index]。
1 2 3 4 5 6 7 let mytuple : [number , string , boolean ] = [42 , "Runoob" , true ];let num = mytuple[0 ];let str = mytuple[1 ];let bool = mytuple[2 ];console .log (num);console .log (str);console .log (bool);
输出:
元组运算 push() :向元组末尾添加元素pop() :从元组末尾移除元素并返回该元素push 实例:
1 2 3 let tuple = [42 , "Hello" ]; tuple.push ("World" );console .log (tuple);
pop 实例:
1 2 3 4 let tuple : [number , string , boolean ] = [42 , "Hello" , true ];let lastElement = tuple.pop ();console .log (lastElement); console .log (tuple);
更新元组 元组是可变的,可通过索引直接赋值更新。
1 2 3 4 let mytuple = [42 , "Runoob" , "Taobao" , "Google" ];console .log ("元组的第一个元素为:" + mytuple[0 ]) mytuple[0 ] = 121 console .log ("元组中的第一个元素更新为:" + mytuple[0 ])
输出:
1 2 元组的第一个元素为:42 元组中的第一个元素更新为:121
解构元组 可将元组元素赋值给变量:
1 2 3 4 let a : [number , string , boolean ] = [42 , "Hello" , true ];const [b,c] = aconsole .log (b)console .log (c)
输出:
标签元组与其他操作 可为元组元素添加标签以提高可读性:
1 let tuple : [id : number , name : string ] = [1 , "John" ];
元组常用于函数返回多个值的场景:
1 2 3 4 5 6 function getUserInfo ( ): [number , string ] { return [1 , "John Doe" ]; }const [userId, userName] = getUserInfo ();console .log (userId); console .log (userName);
使用 as const 将元组视为不可变常量元组:
1 let tuple = [42 , "Hello" ] as const ;
使用 concat 方法连接元组,结果是一个普通数组而非元组:
1 2 3 4 let tuple1 : [number , string ] = [42 , "Hello" ];let tuple2 : [number , string ] = [100 , "World" ];let result = tuple1.concat (tuple2);console .log (result);
使用 slice 方法切片元组,返回新数组:
1 2 3 let tuple : [number , string , boolean ] = [42 , "Hello" , true ];let sliced = tuple.slice (1 );console .log (sliced);
遍历元组可使用 for...of 循环或 forEach 方法:
1 2 3 4 5 let tuple : [number , string , boolean ] = [42 , "Hello" , true ];for (let item of tuple) { console .log (item); } tuple.forEach (item => console .log (item));
通过 Array.from 转换为普通数组后可使用数组方法:
1 2 3 4 let tuple : [number , string , boolean ] = [42 , "Hello" , true ];let array = Array .from (tuple); array.push ("New Element" );console .log (array);
使用剩余运算符合并多个元组:
1 2 3 4 let tuple1 : [number , string ] = [42 , "Hello" ];let tuple2 : [boolean ] = [true ];let extendedTuple : [number , string , ...typeof tuple2] = [42 , "Hello" , ...tuple2];console .log (extendedTuple);
枚举(Enum) 枚举(Enum)类型用于定义数值集合,让开发者为一组数值赋予友好的名字,从而避免在代码中出现"魔术数字",提升可读性与可维护性。
数字枚举 默认情况下,枚举成员从 0 开始编号:
1 2 3 4 5 6 7 8 9 10 enum Direction { Up , Down , Left , Right }const dir : Direction = Direction .Up ;console .log ("方向: " + dir);console .log ("方向名称: " + Direction [0 ]);
输出:
手动赋值 可以手动为枚举成员指定值:
1 2 3 4 5 6 7 8 enum Status { Success = 1 , Error = 2 , Pending = 3 }console .log ("状态: " + Status .Success );console .log ("状态名称: " + Status [1 ]);
输出:
字符串枚举 字符串枚举要求每个成员都必须赋予字符串字面量值:
1 2 3 4 5 6 7 enum Message { Success = "SUCCESS" , Error = "ERROR" , Warning = "WARNING" }console .log ("消息: " + Message .Success );
输出:
常量枚举 使用 const 修饰符可以声明常量枚举,它会在编译时被内联,从而生成更优化的代码:
1 2 3 4 5 6 7 8 const enum Color { Red = "RED" , Green = "GREEN" , Blue = "BLUE" }const favoriteColor : Color = Color .Red ;console .log ("喜欢的颜色: " + favoriteColor);
⚠️ 使用 Vite、esbuild、swc 等基于 isolatedModules 的工具链时,const enum 跨文件不会内联,可能产生运行时问题。新代码建议改用普通 enum 或联合字面量类型,const enum 仅在单文件内或确认编译器支持时使用。
异构枚举 枚举可以混合数字和字符串值,但这种用法并不推荐:
1 2 3 4 5 6 7 enum BooleanLikeHeterogeneousEnum { No = 0 , Yes = "YES" }console .log ("值: " + BooleanLikeHeterogeneousEnum .No );console .log ("字符串值: " + BooleanLikeHeterogeneousEnum .Yes );
枚举成员类型 当所有成员都是字面量值时,单个成员可以作为类型使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 enum ShapeKind { Circle = "circle" , Square = "square" }interface Circle { kind : ShapeKind .Circle ; radius : number ; }interface Square { kind : ShapeKind .Square ; sideLength : number ; }const c : Circle = { kind : ShapeKind .Circle , radius : 10 };console .log ("圆形: " + JSON .stringify (c));
运行时常量枚举 普通枚举在运行时会作为真实对象保留:
1 2 3 4 5 6 7 enum FileAccess { Read = 1 << 1 , Write = 1 << 2 , ReadWrite = Read | Write }console .log ("文件访问: " + FileAccess .ReadWrite );
输出:
枚举类型小结:
数字枚举 :默认从 0 开始,支持手动赋值字符串枚举 :每个成员必须是字符串字面量常量枚举 :使用 const,编译时内联异构枚举 :混合数字与字符串(不推荐)成员类型 :字面量成员可作为类型使用💡 更轻量的现代替代 :当只需一组字符串状态值且不需要反向映射时,可用联合字面量类型代替 enum,更利于 tree-shaking:
1 2 type Status = "success" | "error" | "pending" ;const current : Status = "success" ;
需要反向映射(如 Status[1])或运行时对象迭代时,仍使用 enum。
Map 对象 Map 对象保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者原始值)都可以作为一个键或一个值。Map 是 ES2015 引入的标准内置对象。
创建 Map TypeScript 使用 Map 类型与 new 关键字来创建 Map:
初始化时可以传入键值对数组:
1 2 3 4 let myMap = new Map ([ ["key1" , "value1" ], ["key2" , "value2" ] ]);
Map 的属性与方法 方法 / 属性 说明 map.clear()移除所有键值对 map.set(key, value)设置键值对,返回 Map 本身 map.get(key)返回键对应的值,不存在则返回 undefined map.has(key)判断是否包含该键,返回布尔值 map.delete(key)删除键值对,返回 true/false map.size返回键值对数量 map.keys()返回键的迭代器 map.values()返回值的迭代器 map.entries()返回所有 [key, value] 的迭代器
常用函数签名如下(TS 5.6 起,Map 的 keys()/values()/entries() 返回 MapIterator 类型,Set 对应 SetIterator,均含迭代器辅助方法):
set(key: K, value: V): this — 添加或更新键值对get(key: K): V | undefined — 根据键取值has(key: K): boolean — 判断键是否存在delete(key: K): boolean — 移除键值对clear(): void — 清空所有条目size: number — 条目数量keys(): MapIterator<K> — 返回键的迭代器values(): MapIterator<V> — 返回值的迭代器entries(): MapIterator<[K, V]> — 返回 [key, value] 数组的迭代器forEach(callbackfn: (value: V, key: K, map: Map<K, V>) => void, thisArg?: any): void — 对每个条目执行回调带类型参数的 Map 示例:
1 2 3 4 5 6 7 8 9 10 const map = new Map <string , number >(); map.set ('one' , 1 ); map.set ('two' , 2 );console .log (map.get ('one' )); console .log (map.has ('two' )); map.delete ('one' );console .log (map.size ); map.forEach ((value, key ) => { console .log (key, value); }); map.clear ();console .log (map.size );
完整示例(test.ts),创建一个字符串键、数字值的 nameSiteMapping:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 let nameSiteMapping = new Map <string , number >(); nameSiteMapping.set ("Google" , 1 ); nameSiteMapping.set ("Runoob" , 2 ); nameSiteMapping.set ("Taobao" , 3 );console .log (nameSiteMapping.get ("Runoob" )); console .log (nameSiteMapping.has ("Taobao" )); console .log (nameSiteMapping.has ("Zhihu" )); console .log (nameSiteMapping.size ); console .log (nameSiteMapping.delete ("Runoob" )); console .log (nameSiteMapping); nameSiteMapping.clear ();console .log (nameSiteMapping);
直接运行(tsx test.ts 或 bun test.ts,Node 23.6+ 亦可原生执行 .ts),输出:
1 2 3 4 5 6 7 2 true false 3 true Map { 'Google' = > 1 , 'Taobao' = > 3 } Map { }
迭代 Map Map 中的元素按插入顺序排列,TypeScript 使用 for...of 来迭代:
1 2 3 4 5 6 7 8 9 10 11 12 for (let key of nameSiteMapping.keys ()) { console .log (key); }for (let value of nameSiteMapping.values ()) { console .log (value); }for (let entry of nameSiteMapping.entries ()) { console .log (entry[0 ], entry[1 ]); }for (let [key, value] of nameSiteMapping) { console .log (key, value); }
输出:
1 2 3 4 5 6 7 8 9 10 11 12 Google Runoob Taobao 1 2 3 Google 1 Runoob 2 Taobao 3 Google 1 Runoob 2 Taobao 3
Map 的键可以是任意类型(对象、函数等),比使用普通对象作为键更灵活,是实现缓存、统计、索引等功能的理想选择。
分组:Object.groupBy 与 Map.groupBy ES2024 引入了 Object.groupBy 与 Map.groupBy,TypeScript 5.4 起为其提供了类型支持,可按指定键把数组元素分组,无需手写 reduce:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const inventory = [ { name : "asparagus" , type : "vegetables" , quantity : 9 }, { name : "banana" , type : "fruit" , quantity : 5 }, { name : "goat" , type : "meat" , quantity : 3 }, { name : "cherries" , type : "fruit" , quantity : 12 }, ];const byType = Object .groupBy (inventory, ({ type } ) => type );console .log (byType.vegetables ); const bySize = Map .groupBy (inventory, ({ quantity } ) => quantity > 5 ? "large" : "small" );console .log (bySize.get ("large" ));
Object.groupBy 适合键为字符串/数字字面量的场景;当键是对象或其他类型时,改用 Map.groupBy。
Set 与 WeakMap/WeakSet TypeScript 继承自 JavaScript 的 Set 和 WeakMap 数据结构,提供了更强大的类型支持。这些数据结构在处理唯一值集合、键值对映射、缓存等场景非常有用。
数据结构对比 数据结构 特点 行为 方法 Set 值的集合 值唯一,不重复,可遍历 add/has/delete WeakSet 对象弱引用 不影响 GC,不可遍历 add/has/delete Map 键值对集合 键可以是任意类型,可遍历 set/get/has WeakMap 键弱引用 键必须是对象或非注册 symbol,不影响 GC,不可遍历 set/get/has
应用场景:Set 用于去重、唯一值集合;Map 用于键值映射、缓存;WeakMap/WeakSet 用于内存优化。
Set 提供了自动去重的集合功能,比数组更方便处理唯一值;WeakSet 和 WeakMap 使用弱引用,不会阻止垃圾回收,适用于避免内存泄漏的场景,如缓存 DOM 节点。
Set Set 是值的集合,值唯一,不允许重复。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const numbers = new Set <number >(); numbers.add (1 ); numbers.add (2 ); numbers.add (3 ); numbers.add (1 ); console .log ("Set 大小: " + numbers.size );console .log ("是否包含 2: " + numbers.has (2 )); numbers.forEach (function (value ) { console .log ("值: " + value); });const arr = Array .from (numbers);console .log ("转换为数组: " + arr);
运行结果:
1 2 3 4 5 6 Set 大小: 3 是否包含 2 : true 值: 1 值: 2 值: 3 转换为数组: 1 , 2 , 3
Set 会自动忽略重复的值,这使得它非常适合用于数组去重。
Set 类型注解 可以显式指定 Set 中值的类型,使用 Set<T> 语法指定 Set 中元素的类型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const stringSet : Set <string > = new Set (); stringSet.add ("a" ); stringSet.add ("b" );interface Person { name : string ; }const personSet : Set <Person > = new Set (); personSet.add ({ name : "Alice" }); personSet.add ({ name : "Bob" });console .log ("字符串 Set: " + Array .from (stringSet));console .log ("对象 Set 大小: " + personSet.size );
WeakSet WeakSet 存储对象引用,引用为弱引用(不影响垃圾回收)。WeakSet 只能存储对象或非注册 symbol(ES2023 起),不能存储其他原始值,且不能遍历。类型约束为 WeakKey(object | symbol),其中对象是最常见用法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const weakSet = new WeakSet ();const obj1 = { name : "Alice" };const obj2 = { name : "Bob" }; weakSet.add (obj1); weakSet.add (obj2);console .log ("是否包含 obj1: " + weakSet.has (obj1)); weakSet.delete (obj1);console .log ("删除后是否包含 obj1: " + weakSet.has (obj1));
WeakSet 适合存储需要被垃圾回收的对象。
WeakMap WeakMap 的键是弱引用,不影响垃圾回收。WeakMap 的键必须是对象或非注册 symbol(ES2023 起),不可遍历,常用于缓存 DOM 节点数据——当 DOM 节点被移除时,缓存数据也会被自动清理,避免内存泄漏。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const weakMap = new WeakMap <object , string >();const keyObj = { id : 1 }; weakMap.set (keyObj, "value1" );console .log ("获取值: " + weakMap.get (keyObj));console .log ("是否包含: " + weakMap.has (keyObj)); weakMap.delete (keyObj);console .log ("删除后: " + weakMap.has (keyObj));
实际应用场景 使用 Map 统计数组元素出现次数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function countElements (arr : string [] ): Map <string , number > { const counts = new Map <string , number >(); for (const item of arr) { const currentCount = counts.get (item) ?? 0 ; counts.set (item, currentCount + 1 ); } return counts; }const fruits = ["apple" , "banana" , "apple" , "orange" , "banana" , "apple" ];const result = countElements (fruits); result.forEach (function (count, fruit ) { console .log (fruit + ": " + count); });
运行结果:
1 2 3 apple : 3 banana : 2 orange : 1
注意事项 Set 唯一性 :Set 自动忽略重复值WeakSet/WeakMap :键必须是对象或非注册 symbol,不能遍历Map 键类型 :Map 的键可以是任意类型内存管理 :WeakSet/WeakMap 不阻止垃圾回收选择建议:需要唯一值集合用 Set,需要键值映射用 Map,需要避免内存泄漏用 WeakSet/WeakMap。
对象(Object) 对象是包含一组键值对的实例。值可以是标量、函数、数组、对象等。
对象语法模板 1 2 3 4 5 6 7 const object_name = { key1 : "value1" , key2 : "value" , key3 : function ( ) { }, key4 :["content1" , "content2" ] }
对象实例:
1 2 3 4 5 6 7 const sites = { site1 :"Runoob" , site2 :"Google" };console .log (sites.site1 )console .log (sites.site2 )
输出:
TypeScript 类型模板 在 JavaScript 中定义对象后可以动态添加方法,但 TypeScript 中的对象必须是特定类型的实例,因此需要预先在类型模板中声明方法。
1 2 3 4 5 6 7 8 9 const sites = { site1 : "Runoob" , site2 : "Google" , sayHello : function ( ) { } }; sites.sayHello = function ( ) { console .log ("hello " + sites.site1 ); }; sites.sayHello ();
输出:
对象作为参数传递给函数 1 2 3 4 5 6 7 8 9 const sites = { site1 :"Runoob" , site2 :"Google" , };const invokesites = function (obj : { site1:string , site2 :string } ) { console .log ("site1 :" +obj.site1 ) console .log ("site2 :" +obj.site2 ) }invokesites (sites)
输出:
1 2 site1 :Runoob site2 :Google
鸭子类型(Duck Typing) 鸭子类型是动态类型的一种风格,是多态的一种形式。在这种风格中,一个对象有效的语义不是由继承自特定的类或实现特定的接口决定,而是由当前方法和属性的集合决定。
核心表述:当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。
在鸭子类型中,关注点在于对象的行为能做什么,而不是关注对象所属的类型。在不使用鸭子类型的语言中,可以编写一个函数接受类型为"鸭子"的对象并调用其"走"和"叫"方法;在使用鸭子类型的语言中,这样的函数可以接受任意类型的对象并调用其"走"和"叫"方法。如果这些方法不存在,将引发运行时错误。
TypeScript 接口与鸭子类型示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 interface IPoint { x :number y :number }function addPoints (p1 :IPoint ,p2 :IPoint ):IPoint {const x = p1.x + p2.x const y = p1.y + p2.y return {x :x,y :y} }const newPoint = addPoints ({x :3 ,y :4 },{x :5 ,y :1 })const newPoint2 = addPoints ({x :1 },{x :4 ,y :3 })
只要传入的对象具备接口所要求的 x 和 y 属性,TypeScript 就认为它符合 IPoint 类型,这正是鸭子类型在静态类型系统中的体现。