如何让TypeScript在if语句中识别条件类型
如何让TypeScript根据事件类型自动推断Payload类型
我们定义了条件事件类型,能根据事件类型提示对应的Payload结构:
type EventType = 'created' | 'updated' | 'deleted' type EventPayload<T> = T extends 'created' ? { somethingForCreatedEvent: string } : T extends 'updated' ? { somethingForUpdatedEvent: string } : T extends 'deleted' ? { somethingForDeletedEvent: string } : never
在触发事件的trigger函数里,已经实现了强类型校验:
function trigger<T extends EventType>(type: T, payload: EventPayload<T>) { // 发送事件逻辑 } trigger('created', { somethingForCreatedEvent: "test" }) // 正确提示仅需传入somethingForCreatedEvent trigger('updated', { somethingForUpdatedEvent: "test" }) // 正确提示仅需传入somethingForUpdatedEvent trigger('deleted', { somethingForDeletedEvent: "test" }) // 正确提示仅需传入somethingForDeletedEvent
遇到的问题
接收事件时,无法获得对应的强类型支持。接收的事件格式为{ type: EventType; payload: EventPayload<...> },但req.body初始类型为any,即使通过判断type字段,payload仍为any/unknown:
// 从队列接收的事件格式:{ type: EventType; payload: EventPayload<...> } const eventPayload = req.body // 此时类型为any if (eventPayload.type === 'created') { eventPayload.payload // 类型为any/unknown,无法获得正确提示 }
之前尝试的辅助函数效果不符合预期:
type RawEvent<T> = { type: T; payload: EventPayload<T>; } function getEventPayload<T extends EventType>(rawEvent: any): RawEvent<T> { return rawEvent } const event = getEventPayload(rawEvent) if (event.type === 'created') { // type字段能正确提示为'created' event.payload // 但payload类型是所有EventPayload的联合类型,而非仅created对应的类型 }
解决方案
要让TypeScript能通过type字段的判断自动收窄payload的类型,需要定义一个联合类型的事件结构,而非泛型的RawEvent<T>:
1. 定义联合事件类型
// 生成所有事件类型的联合类型 type Event = { [K in EventType]: { type: K; payload: EventPayload<K>; }; }[EventType];
2. 编写类型守卫函数(推荐)
添加运行时校验的类型守卫,确保输入符合Event类型,同时让TypeScript自动收窄类型:
// 类型守卫函数,判断输入是否为合法的Event类型 function isEvent(raw: any): raw is Event { if (!raw || typeof raw !== 'object') return false; // 先校验type字段是否为合法的EventType if (!(['created', 'updated', 'deleted'] as const).includes(raw.type)) return false; // 再根据type校验对应的payload结构 switch (raw.type) { case 'created': return typeof raw.payload?.somethingForCreatedEvent === 'string'; case 'updated': return typeof raw.payload?.somethingForUpdatedEvent === 'string'; case 'deleted': return typeof raw.payload?.somethingForDeletedEvent === 'string'; default: return false; } }
3. 使用示例
现在使用时,TypeScript会自动根据type字段收窄payload的类型:
const eventPayload = req.body; if (isEvent(eventPayload)) { if (eventPayload.type === 'created') { // 正确提示somethingForCreatedEvent字段 console.log(eventPayload.payload.somethingForCreatedEvent); } else if (eventPayload.type === 'updated') { console.log(eventPayload.payload.somethingForUpdatedEvent); } else if (eventPayload.type === 'deleted') { console.log(eventPayload.payload.somethingForDeletedEvent); } }
简化方案(无运行时校验)
如果不需要运行时校验,可直接使用类型断言,但这种方式跳过了合法性检查,存在风险:
const event = req.body as Event; if (event.type === 'created') { console.log(event.payload.somethingForCreatedEvent); // 类型正确,能获得提示 }
内容的提问来源于stack exchange,提问作者Ollie
相关产品推荐
相关产品推荐

