TypeScript:如何为多组件映射的实体生成强类型?
解决方案
要实现将传入的组件数组推导为对应实体类型数组的需求,你需要调整类型工具和函数的类型定义,核心是正确遍历组件数组的每个元素并提取name和type来构建实体类型。
1. 修正类型工具
替换你之前的UnionToIntersection,使用下面的ComponentTupleToEntity工具类型,它会遍历组件数组的每个元素,将每个组件的name作为实体对象的键,type作为对应键的类型:
type ComponentTupleToEntity<T extends Component<any>[]> = { [K in T[number] as K['name']]: K['type'] }[]
2. 调整queryEntities函数类型
将函数的返回类型指定为上述工具类型,确保类型推导准确:
function queryEntities<T extends Component<any>[]>(...components: T): ComponentTupleToEntity<T> { // 你的实体过滤逻辑示例: // 假设这里有一个全局实体列表 const allEntities = []; const filteredEntities = allEntities.filter(entity => components.every(comp => entity.hasOwnProperty(comp.name)) ); return filteredEntities as ComponentTupleToEntity<T>; }
3. 优化createComponent(可选但推荐)
为了让name的类型推导为具体的字面量类型(而非宽泛的string),可以修改createComponent函数,确保返回的组件对象保留name的字面量类型:
function createComponent<T, N extends string>(name: N, type: T): { name: N; type: T } { return { name, type } as const; }
这样当你创建组件时:
const TransformComponent = createComponent("transform", { x: 0, y: 0 } as const); const BodyComponent = createComponent("body", { height: 100, width: 100 } as const);
类型推导会更精准,ComponentTupleToEntity能准确识别name的具体值。
验证效果
现在调用queryEntities时,返回的类型会完全符合预期:
const entities = queryEntities(BodyComponent, TransformComponent); for (const entity of entities) { console.log(entity.body.height); // 类型为number console.log(entity.transform.x); // 类型为number }
此时entities的类型就是:
{ body: { height: number; width: number }; transform: { x: number; y: number }; }[]
为什么之前的实现失败?
你之前的UnionToIntersection工具类型错误地将整个组件数组作为单个元素进行infer,而没有遍历数组的每个元素。正确的做法是通过T[number]获取数组元素的联合类型,再用as关键字将每个元素的name映射为对象的键。
内容的提问来源于stack exchange,提问作者Jonathon Orsi
相关产品推荐
相关产品推荐

