TypeScript中如何提取泛型类型的泛型参数或对象属性类型以约束PubSub类subscribe方法的topic参数
当然可以实现这两种类型约束的需求!TypeScript的类型系统提供了灵活的工具来帮你提取对象属性类型和泛型参数类型,下面分两种情况来解决你的问题:
1. 提取对象指定属性的类型(对应你想要的TypeOfObjectKey<T, 'topic'>)
这个需求其实TypeScript内置就支持,用索引访问类型就能直接实现,甚至不需要额外定义工具类型。你可以直接用T['topic']来获取泛型T中topic属性的类型。
如果想封装成通用的工具类型方便复用,也可以这么写:
// 通用工具类型:提取对象T中属性K的类型 type TypeOfObjectKey<T, K extends keyof T> = T[K];
然后修改你的PubSub类,把subscribe的参数类型改成这个:
class PubSub<T extends Message = Message> { publish(message: T): void { // 实现逻辑 } // 直接用索引访问类型,简洁高效 subscribe(topic: T['topic']): void { // 实现逻辑 } // 或者用封装好的工具类型 // subscribe(topic: TypeOfObjectKey<T, 'topic'>): void { /* ... */ } }
当你传入CustomMessage作为泛型参数时,subscribe的topic参数会自动被约束为'orders.create' | 'orders.update',传入其他字符串会直接触发类型错误,完美符合你的需求!
2. 提取泛型类型的泛型参数(对应你想要的GenericOf<T, 0>)
TypeScript没有内置的直接获取泛型参数的方法,但可以通过条件类型推断来实现。针对你的Message接口,我们可以定义一个专门的工具类型来提取它的第一个泛型参数E:
// 提取Message接口的第一个泛型参数E type GetMessageTopic<T extends Message> = T extends Message<infer E, infer _> ? E : never;
然后在PubSub中使用这个类型:
class PubSub<T extends Message = Message> { publish(message: T): void { /* ... */ } subscribe(topic: GetMessageTopic<T>): void { /* ... */ } }
这个方法的原理是利用infer关键字,在条件类型中推断出Message的第一个泛型参数类型。如果后续你需要提取第二个泛型参数P,也可以用类似的方式:
type GetMessageParams<T extends Message> = T extends Message<infer _, infer P> ? P : never;
如果你想做一个更通用的GenericOf工具类型(支持提取任意泛型的第N个参数),虽然可以实现,但会比较复杂(因为不同泛型的参数数量和结构差异很大),针对你的场景,专门针对Message的推断方案更简洁可靠。
完整示例验证
我们把所有代码整合起来,测试一下类型约束的效果:
interface Message<E extends string = string, P = any> { topic: E; params: P; } interface OrderParams { userId: string; orderId: string; } interface CreateOrderMessage extends Message<'orders.create', OrderParams> {} interface UpdateOrderMessage extends Message<'orders.update', OrderParams> {} type CustomMessage = CreateOrderMessage | UpdateOrderMessage; class PubSub<T extends Message = Message> { publish(message: T): void {} subscribe(topic: T['topic']): void {} } const pubSub = new PubSub<CustomMessage>(); // ✅ 合法:topic是允许的值 pubSub.subscribe('orders.create'); pubSub.subscribe('orders.update'); // ❌ 类型错误:'orders.delete'不在允许的范围内 pubSub.subscribe('orders.delete');
这样就能完美实现你想要的严格类型约束了!
内容的提问来源于stack exchange,提问作者TheMisir
相关产品推荐
相关产品推荐

