如何限制泛型为键取自值属性的Map类型?
实现键与值指定属性相等的Map类型及泛型约束
我来帮你搞定这个问题!你当前的IndexMap类型只定义了键的类型是V[I],但没有强制每个键对应的V实例的指定属性值必须和键本身完全匹配,这就是为什么不匹配也不会触发编译错误的原因。下面是完整的解决方案:
1. 修正IndexMap类型定义
我们需要修改类型,让每个键P对应的V对象的指定属性I必须严格等于P:
type IndexMap<I extends keyof V, V extends Record<I, string>> = { [P in V[I]]: V & Record<I, P>; };
这里的核心是V & Record<I, P>:它将原类型V与一个强制I属性等于当前键P的类型交叉,确保每个键值对的键和值的指定属性完全一致。
测试修正后的类型
现在再写测试代码,不匹配的情况会直接触发编译错误:
const test: IndexMap<'prop', { ok: number, prop: string }> = { someVal: { ok: 1, prop: 'someVal' }, // ✅ 符合要求,无错误 wrongVal: { ok: 2, prop: 'otherVal' } // ❌ 编译错误:类型"otherVal"不能赋值给类型"wrongVal" };
2. 用于泛型约束
接下来把这个类型用到你的Service类泛型约束中。首先假设你的CustomizedPreDefinedApiModel是类似这样的预定义类型:
// 示例预定义模型 type CustomizedPreDefinedApiModel = { name: string; id: number; // 其他业务属性 };
然后定义Service类,用IndexMap约束泛型T:
class Service<T extends IndexMap<'name', CustomizedPreDefinedApiModel>> { // 示例方法:根据名称获取模型,类型自动校验 getModel(name: keyof T): T[keyof T] { // 这里可以写实际业务逻辑,类型会自动推导 return {} as T[keyof T]; } }
使用示例
当你实例化Service时,传入的类型必须符合IndexMap的约束,否则会报错:
// ✅ 合法的实例化:每个键对应的值的name属性和键完全一致 const validService = new Service<{ user: { name: 'user', id: 1 }, product: { name: 'product', id: 2 } }>(); validService.getModel('user'); // 返回类型:{ name: 'user', id: 1 } validService.getModel('product'); // 返回类型:{ name: 'product', id: 2 } validService.getModel('invalid'); // ❌ 编译错误:"invalid"不是keyof T的成员 // ❌ 非法实例化:键"order"对应的值的name属性不匹配 const invalidService = new Service<{ order: { name: 'orderItem', id: 3 } }>(); // 编译错误:类型"{ name: 'orderItem', id: 3 }"不能赋值给类型"{ name: 'order', id: number }"
3. 扩展:支持非字符串类型的键
如果你的键/属性值需要支持number或symbol类型,可以把约束放宽到PropertyKey(TypeScript内置类型,代表string | number | symbol):
type IndexMap<I extends keyof V, V extends Record<I, PropertyKey>> = { [P in V[I]]: V & Record<I, P>; };
这样就可以处理更多场景啦!
内容的提问来源于stack exchange,提问作者Luciens
相关产品推荐
相关产品推荐

