TypeScript中创建Hash of Arrays(或等效结构)的最简方案
问题:在TypeScript中实现动态属性的Hash of Arrays结构
需求说明
需要创建一个Hash of Arrays(或等效数据结构),用于收集未知名称的属性(属性名作为键),每个属性对应存储一组拥有该属性的对象。核心逻辑类似以下代码,但需要在TypeScript中完成正确的类型定义与初始化,无需提前枚举所有可能的属性:
const currentProperties = currentObject.getProperties(); // 假设getProperties返回有效属性数组 currentProperties.forEach( (v) => { HoA[ v ].push( currentObject ); });
此前已实现过提前指定属性的场景(如下代码),但本次需要支持完全未知的属性值:
if (currentProperties.includes(Properties.exampleOne)) { this.exampleGroupOne.push(currentObject); }
补充背景:处理一批拥有getProperties()方法的对象,该方法返回零个或多个属性的数组。后续需要按属性(外层循环)、对应对象(内层循环)处理数据,因此选择Hash of Arrays结构。
中间尝试的问题
以下是接近需求但存在类型错误的代码:
class Alice { myProps(): string[] { return ["Apple"]; } } class Bob { myProps(): string[] { return ["Banana"]; } } class Charlie { myProps(): string[] { return ["Apple", "Banana"]; } } // 类型定义错误:值类型应为对象数组,而非string const FruitBasket:{ [prop: string]: string} = {} const myAlice = new Alice(); const myBob = new Bob(); const myCharlie = new Charlie(); const Objects = [myAlice, myBob, myCharlie]; for (const currentObject of Objects) { const fruits = currentObject.myProps(); // 报错:push不存在于string类型 fruits.forEach( (v) => { FruitBasket[v].push(currentObject);}); }
错误原因是FruitBasket的类型定义错误,将值类型设为string,但实际需要存储对象数组。
最终解决方案
通过抽象类统一对象类型、修正结构类型定义,并使用空值合并运算符初始化数组,实现需求:
abstract class JustSomeGuy { myProps(): string[] { return []; } myName(): string { return ''; } } class Alice extends JustSomeGuy { myProps(): string[] { return ["Apple"]; } myName(): string { return 'Alice'; } } class Bob extends JustSomeGuy { myProps(): string[] { return ["Banana"]; } myName(): string { return 'Bob'; } } class Charlie extends JustSomeGuy { myProps(): string[] { return ["Apple", "Banana"]; } myName(): string { return 'Charlie'; } } // 正确定义类型:键为string,值为JustSomeGuy类型的数组 const FruitBasket:{ [prop: string]: JustSomeGuy[]} = {} const myAlice = new Alice(); const myBob = new Bob(); const myCharlie = new Charlie(); const Objects = [myAlice, myBob, myCharlie]; // 使用??=确保属性不存在时初始化空数组,再push对象 for (const currentObject of Objects) { const fruits = currentObject.myProps(); fruits.forEach( (v) => { (FruitBasket[v] ??= []).push(currentObject);}); } // 遍历验证结果 for (let key in FruitBasket){ let value = FruitBasket[key]; for (let i = 0; i < value.length; i++){ console.log("In key: " + key + " the ith element [i = " + i + "] is: " + value[i].myName() ); } }
关键要点
- 用抽象类
JustSomeGuy统一所有对象的类型,确保它们都实现了必要的方法 - 正确定义
FruitBasket的类型为{ [prop: string]: JustSomeGuy[] },明确每个键对应对象数组 - 使用
??=运算符(空值合并赋值),在属性首次出现时自动初始化空数组,避免Cannot read property 'push' of undefined错误
内容的提问来源于stack exchange,提问作者drh
相关产品推荐
相关产品推荐

