如何让TypeScript中createEventOnTopic函数的第一个参数约束第二个参数的合法事件类型?
实现Topic与Event的类型关联
当然可以实现!我们可以利用TypeScript的泛型和映射类型,让第一个参数topic的取值直接约束第二个参数message中event的可选值,在编译阶段就拦截不匹配的组合。
步骤1:建立Topic到Event的类型映射
首先我们需要明确每个Topic对应的合法Event集合,用一个映射类型把它们关联起来:
const topics = ['topic-1', 'topic-2'] as const; export type Topic = typeof topics[any]; const eventsTopic1 = ['add_something', 'remove_something'] as const; export type EventTopic1 = typeof eventsTopic1[any]; const eventsTopic2 = ['add_anything', 'remove_anything'] as const; export type EventTopic2 = typeof eventsTopic2[any]; // 核心:定义Topic与对应Event类型的映射关系 type TopicToEventMap = { 'topic-1': EventTopic1; 'topic-2': EventTopic2; };
步骤2:改造Message为泛型接口
把原来的Message接口改成泛型,让它的event字段类型可以动态传入:
export interface Message<E extends string> { event: E; data: any; }
步骤3:用泛型约束函数参数
修改createEventOnTopic函数,通过泛型捕获传入的具体Topic值,再通过映射类型获取对应的Event类型,从而约束message的结构:
function createEventOnTopic<T extends Topic>(topic: T, message: Message<TopicToEventMap[T]>): void { }
测试效果
现在调用函数时,TypeScript会自动校验event是否匹配所选的topic:
createEventOnTopic('topic-1', { event: 'add_something', data: 'any' }); // ✅ 完全合法 createEventOnTopic('topic-1', { event: 'add_anything', data: 'any' }); // ❌ 报错:类型'"add_anything"'不能赋值给类型'"add_something" | "remove_something"' createEventOnTopic('topic-2', { event: 'add_anything', data: 'any' }); // ✅ 完全合法
可选优化:让Data类型也与Event关联
如果不同的Event需要对应不同的Data结构,我们可以进一步扩展映射关系,让类型约束更严谨:
// 扩展映射,包含每个Event对应的Data类型 type TopicToEventDetailsMap = { 'topic-1': { 'add_something': { id: string; name: string }, 'remove_something': { id: string } }; 'topic-2': { 'add_anything': { content: string }, 'remove_anything': { uuid: string } }; }; // 重新定义Message接口,关联Event和Data类型 export interface Message<T extends Topic, E extends keyof TopicToEventDetailsMap[T]> { event: E; data: TopicToEventDetailsMap[T][E]; } // 调整函数,支持更细粒度的类型约束 function createEventOnTopic<T extends Topic, E extends keyof TopicToEventDetailsMap[T]>( topic: T, message: Message<T, E> ): void { } // 测试更严谨的约束 createEventOnTopic('topic-1', { event: 'add_something', data: { id: '1', name: 'test' } }); // ✅ 合法 createEventOnTopic('topic-1', { event: 'add_something', data: { id: '1' } }); // ❌ 报错:缺少必填字段'name'
这样不仅能约束Event与Topic的匹配,还能确保每个Event传入的Data结构完全符合要求。
内容的提问来源于stack exchange,提问作者Iago Bontempo
相关产品推荐
相关产品推荐

