基于键类型自动收窄值类型的TypeScript实现问询
问题
我定义了以下类型和Save函数:
type Collection<K extends string, EntityType> = { save(key: K, value: EntityType): void; get(key: K, value: EntityType): EntityType[]; all(): EntityType[]; }; type Domain = { [key: string]: Collection<any, any>; }; function Save<D extends Domain, K extends string, V>( dataStore: D, key: K, value: V ) { if (key.startsWith("orders/")) { dataStore.orders.save(key, value); } else if (key.startsWith("customers/")) { dataStore.customers.save(key, value); } //... }
我希望按如下方式使用Save()函数:使其能够通过查找CommerceDomain中的Collection<>成员,自动将Save()调用中的value参数类型收窄为对应的实体类型:
type CustomerId = `customers/${string}`; type OrderId = `orders/${string}`; type ProductId = `products/${string}`; interface Customer { customer: string } interface Order { order: string } interface Product { product: string } declare const customer: Customer; declare const order: Order; declare const product: Product; type CommerceDomain = { orders: Collection<OrderId, Order>; customers: Collection<CustomerId, Customer>; products: Collection<ProductId, Product>; }; declare const store: CommerceDomain; save(store, "orders/1", {/* 应自动推导为Order类型 */}); save(store, "customers/1", {/* 应自动推导为Customer类型 */});
请问如何实现这一需求?
解决方案
要实现根据key自动推导value的类型,需修改Save函数的泛型约束,利用条件类型和索引类型建立key与对应Collection实体类型的关联,同时优化函数逻辑的扩展性。
修改后的完整实现
type Collection<K extends string, EntityType> = { save(key: K, value: EntityType): void; get(key: K): EntityType[]; // 修正原get方法的多余参数问题 all(): EntityType[]; }; type Domain = { [key: string]: Collection<string, any>; }; // 工具类型:从Domain中提取key前缀对应的实体类型 type EntityForKey<D extends Domain, K extends string> = K extends `${infer Prefix}/${string}` ? Prefix extends keyof D ? D[Prefix] extends Collection<any, infer E> ? E : never : never : never; function save< D extends Domain, K extends string, V extends EntityForKey<D, K> >( dataStore: D, key: K, value: V ) { // 提取key前缀并调用对应Collection的save方法,无需硬编码判断 const prefix = key.split('/')[0] as keyof D; dataStore[prefix].save(key as any, value); }
核心逻辑说明
- 工具类型
EntityForKey:- 通过模板字符串类型
${infer Prefix}/${string}提取key中的前缀(如orders/1中的orders) - 验证前缀是否属于
Domain的键,再从对应Collection中提取实体类型E,完成类型映射
- 通过模板字符串类型
- 泛型约束优化:
- 将
V的类型约束为EntityForKey<D, K>,TypeScript会自动根据传入的key推导出value必须匹配的实体类型
- 将
- 函数逻辑简化:
- 移除硬编码的
if-else判断,通过动态提取前缀调用对应Collection的方法,新增products这类集合时无需修改函数逻辑
- 移除硬编码的
使用验证
现在调用save函数时,TypeScript会自动完成类型推导与校验:
// 正确:value自动推导为Order类型 save(store, "orders/1", { order: "123" }); // 正确:value自动推导为Customer类型 save(store, "customers/1", { customer: "Alice" }); // 错误:类型不匹配,TypeScript会直接报错 save(store, "orders/1", { customer: "Bob" });
内容的提问来源于stack exchange,提问作者Serguzest
相关产品推荐
相关产品推荐

