如何在TypeScript中为添加的迭代器正确进行类型标注?
为数组字典添加类型安全的迭代器
要实现类型安全的标注,核心是利用TypeScript的泛型和映射类型捕获输入对象的结构,推导迭代器返回值的具体类型。以下是完整的类型标注方案:
完整代码实现
const obj = { x: [1, 2, 3], y: [4, 5, 6], z: ["a", "b", "c"] }; const arr = [ { x: 1, y: 4, z: "a" }, { x: 2, y: 5, z: "b" }, { x: 3, y: 6, z: "c" }, ]; // 定义符合IteratorResult规范的结束标识 const DONE: IteratorResult<never, void> = { done: true, value: undefined }; // 用泛型T捕获输入对象的具体结构 const addLongIterator = <T extends Record<string, readonly any[]>>(data: T) => { Object.defineProperty(data, Symbol.iterator, { value: function () { const keys = Object.keys(data) as Array<keyof T>; let i = 0; return { next(): IteratorResult<{ [K in keyof T]: T[K][number] }, void> { if (!(i < data[keys[0]].length)) return DONE; // 用映射类型标注返回对象的精确类型 const value = {} as { [K in keyof T]: T[K][number] }; for (const key of keys) { value[key] = data[key][i]; } i++; return { done: false, value }; }, }; }, }); // 返回类型:原对象类型 + 带具体类型的迭代器 return data as T & { [Symbol.iterator](): Iterator<{ [K in keyof T]: T[K][number] }, void, undefined>; }; }; const objIt = addLongIterator(obj); // 此时objIt的迭代元素会被正确推导为 { x: number; y: number; z: string } for (const x of objIt) console.log(x); for (const x of arr) console.log(x);
关键类型标注解析
泛型约束
- 用
<T extends Record<string, readonly any[]>>定义泛型T,约束输入对象的每个属性都是数组(readonly any[]兼容只读数组场景),同时让TypeScript自动捕获输入对象的具体键名和数组元素类型。
- 用
迭代器元素类型推导
- 通过映射类型
{ [K in keyof T]: T[K][number] }生成迭代器返回的对象类型:遍历T的所有键K,取对应数组T[K]的元素类型T[K][number],最终组合成和数组元素结构完全一致的对象类型。 Iterator<ItemType, ReturnType, NextInputType>参数说明:ItemType:迭代器返回的元素类型(即上述映射类型)ReturnType:迭代结束时的返回值类型(这里为void,对应done状态的value为undefined)NextInputType:next()方法接受的参数类型(这里无需参数,所以设为undefined)
- 通过映射类型
内部变量类型修正
- 将
keys断言为Array<keyof T>,确保遍历的键都是输入对象的合法键,避免类型不匹配。 - 用映射类型标注
value,替代原来的Record<string, any>,保证每个键的类型和对应数组元素严格一致。
- 将
结束标识类型规范
- 定义
DONE为IteratorResult<never, void>,符合TypeScript迭代器结果的官方规范,消除类型歧义。
- 定义
内容的提问来源于stack exchange,提问作者Adam B.
相关产品推荐
相关产品推荐

