TypeScript中使用字符串模板联合类型访问记录属性的问题
解决方案与解析
问题本质的准确性
你的这个理解完全准确。TypeScript处理联合类型索引时会做最严格的兼容性检查:当type是EntityName联合类型时,store[type]会被推断为三个Record类型的联合;而entityId是EntityId联合类型(三个模板ID的合集)。此时编译器无法确认entityId的前缀和substore的索引类型是否匹配,因此抛出错误。
类型安全的实现方式
方法1:泛型+类型关联(推荐)
通过建立EntityName与对应EntityId的类型映射,并用泛型绑定输入输出的类型关系,让编译器识别出type和entityId的对应性:
// 建立实体名称与对应ID类型的映射 type EntityIdMap = { ActivityTemplate: ActivityTemplateId FacetTemplate: FacetTemplateId DataTemplate: DataTemplateId } // 重构typeofId为泛型函数,添加类型断言让编译器识别关联 export const typeofId = <T extends string>(str: T): T extends ActivityTemplateId ? "ActivityTemplate" : T extends FacetTemplateId ? "FacetTemplate" : T extends DataTemplateId ? "DataTemplate" : "NO_TYPE" => { const prefix = str.slice(0, -36) if (prefix === "act-t-") return "ActivityTemplate" as any if (prefix === "fct-t-") return "FacetTemplate" as any if (prefix === "dta-t-") return "DataTemplate" as any return "NO_TYPE" as any } // 泛型版本的get函数,自动推导类型 const get = <T extends EntityId>(store: Store) => (entityId: T) => { const type = typeofId(entityId) if (type === "NO_TYPE") return null // 此时编译器能确认type对应的substore索引类型与entityId匹配 return store[type]?.[entityId] ?? null }
方法2:类型断言(快速解决)
如果不想重构现有函数,可以直接给substore添加精确的类型断言,明确entityId与substore索引类型的匹配性:
const get = (store: Store) => (entityId: EntityId) => { const type = typeofId(entityId) if (type === "NO_TYPE") return null // 断言substore的索引类型就是当前的entityId类型 const substore = store[type] as Record<typeof entityId, Template> return substore[entityId] ?? null }
这种方式需要确保typeofId的逻辑绝对正确(即返回的type确实对应entityId的前缀),否则会存在类型安全隐患。
最终效果
两种方法都能让get函数返回类型为Template | null,全程无需使用any类型,完全符合类型安全要求。
内容的提问来源于stack exchange,提问作者bsluther
相关产品推荐
相关产品推荐

