TypeScript中基于数组值设置对象键及空数组默认值的最优方法
优化后的函数式实现(解决第一个疑问)
你原有的思路是正确的,我们可以通过泛型和内置类型工具去掉冗余的any和不安全的类型断言,更符合TS的类型安全要求:
// 1. 定义源数组,用const断言提取字面量类型 const arr = [{ name: 'key1' }, { name: 'key2' }] as const; type ArrKey = typeof arr[number]['name']; // 2. 定义数组元素的具体类型(比宽泛的object更安全,可按需扩展) type EntryItem = { pushedItem: string }; // 3. 通用的空对象生成函数,泛型支持自定义键类型和数组元素类型 const createEmptyEntry = <K extends string, T>(keys: readonly K[]): Record<K, T[]> => { return keys.reduce((acc, key) => { acc[key] = [] as T[]; return acc; }, {} as Record<K, T[]>); } // 4. 生成实例,自动获得完整类型提示 const keys = arr.map(item => item.name); const emptyEntry = createEmptyEntry<ArrKey, EntryItem>(keys); // 调用时自动校验键名、数组元素结构 emptyEntry.key1.push({ pushedItem: 'pushedItem1' }); // 写错键名/元素属性会直接报错:emptyEntry.key3.push(...) 、emptyEntry.key1.push({ wrongProp: '1' })
类实现方案(解决第二个疑问)
你原有类实现的核心问题是TS无法识别动态生成的实例属性,我们可以通过泛型绑定键类型、增加索引声明来实现完全的类型安全:
const arr = [{ name: 'key1' }, { name: 'key2' }] as const; type ArrKey = typeof arr[number]['name']; type EntryItem = { pushedItem: string }; class Entry<K extends string, T> { // 声明实例的索引结构,告诉TS动态属性的类型 [key: string]: T[] | ((key: K, item: T) => void); constructor(keys: readonly K[]) { // 初始化所有属性为空数组 keys.forEach(key => { this[key] = [] as T[]; }); } // 封装push方法,自动校验键名和元素类型 add(key: K, item: T) { this[key].push(item); } } // 使用示例 const keys = arr.map(item => item.name); const entry = new Entry<ArrKey, EntryItem>(keys); // 两种调用方式都支持类型校验 entry.add('key1', { pushedItem: 'test1' }); entry.key1.push({ pushedItem: 'test2' });
核心优化点
- 全程避免使用
any,所有类型自动推导,写错键名、数组元素结构都会在编译阶段报错 - 泛型设计支持复用,你可以根据业务需求自定义数组元素的类型,不需要硬写死结构
- 没有冗余的类型断言,仅有的初始值断言是完全安全的
内容的提问来源于stack exchange,提问作者MarleenEliza
相关产品推荐
相关产品推荐

