如何从TypeScript常量提取分类与触发器名称的联合类型?
提取TypeScript常量中的分类与触发器名称联合类型
问题背景
我定义了如下TypeScript接口:
export interface GroupByCategoryAndTrigger { [key: string]: { label: string; name: string; triggers: { [key: string]: { name: string, label: string }; } } }
以及实现该接口的常量:
export const CATEGORIES: GroupByCategoryAndTrigger = { reservation: { name: 'Reservation' as const, label: 'Reservation', triggers: { created: { name: 'created', label: 'Reservation created' }, confirmation: { name: 'confirmation' as const, label: 'Reservation confirmed', }, edited: { name: 'edited' as const, label: 'Reservation edited', }, canceled: { name: 'canceled' as const, label: 'Reservation cancelled', }, reminder1: { name: 'reminder_1' as const, label: 'Reminder 1', }, reminder2: { name: 'reminder_2' as const, label: 'Reminder 2', }, reminder3: { name: 'reminder_3' as const, label: 'Reminder 3', }, payment: { name: 'payment' as const, label: 'Awaiting Payment', }, feedbackRequest: { name: 'feedback_request' as const, label: 'Feedback request', }, }, }, queue: { name: 'Offline Waitlist' as const, label: 'Queue', triggers: { waitlistConfirmation: { name: 'waitlist_confirmation' as const, label: 'Waitlist confirmation', }, tableConfirmation: { name: 'table_confirmation' as const, label: 'Table confirmation' }, }, }, };
我希望提取所有分类名称和触发器名称的联合类型,当前的写法如下:
export type CategoriesValues = typeof CATEGORIES[keyof GroupByCategoryAndTrigger]['name']; // 期望得到 "Reservation" | "Offline Waitlist" export type TriggerValues = typeof CATEGORIES[keyof GroupByCategoryAndTrigger]['triggers'][string]['name']; // 期望得到 "confirmation" | "canceled" | ... 所有触发器名称
作为TypeScript新手,我想寻求正确的解决方案。
问题原因
问题出在你给CATEGORIES标注了GroupByCategoryAndTrigger接口,这会让TypeScript把所有name的类型统一当成接口里定义的string,直接丢掉了你用as const指定的具体字面量信息,所以拿不到预期的联合类型。
正确解决方案
方案1:移除接口标注,锁定字面量类型
如果你不需要修改这个常量,最简单的办法是去掉CATEGORIES的接口标注,换成as const来锁定所有字面量类型——这样TypeScript会完整保留每个name的具体字符串值:
export const CATEGORIES = { reservation: { name: 'Reservation' as const, label: 'Reservation', triggers: { created: { name: 'created', label: 'Reservation created' }, confirmation: { name: 'confirmation' as const, label: 'Reservation confirmed', }, edited: { name: 'edited' as const, label: 'Reservation edited', }, canceled: { name: 'canceled' as const, label: 'Reservation cancelled', }, reminder1: { name: 'reminder_1' as const, label: 'Reminder 1', }, reminder2: { name: 'reminder_2' as const, label: 'Reminder 2', }, reminder3: { name: 'reminder_3' as const, label: 'Reminder 3', }, payment: { name: 'payment' as const, label: 'Awaiting Payment', }, feedbackRequest: { name: 'feedback_request' as const, label: 'Feedback request', }, }, }, queue: { name: 'Offline Waitlist' as const, label: 'Queue', triggers: { waitlistConfirmation: { name: 'waitlist_confirmation' as const, label: 'Waitlist confirmation', }, tableConfirmation: { name: 'table_confirmation' as const, label: 'Table confirmation' }, }, }, } as const;
然后基于推导的类型提取联合类型:
// 提取分类名称联合类型 export type CategoriesValues = typeof CATEGORIES[keyof typeof CATEGORIES]['name']; // 结果:"Reservation" | "Offline Waitlist" // 提取触发器名称联合类型 export type TriggerValues = typeof CATEGORIES[keyof typeof CATEGORIES]['triggers'][keyof typeof CATEGORIES[keyof typeof CATEGORIES]['triggers']]['name']; // 结果:"created" | "confirmation" | "edited" | "canceled" | "reminder_1" | "reminder_2" | "reminder_3" | "payment" | "feedback_request" | "waitlist_confirmation" | "table_confirmation"
方案2:保留接口约束,同时提取字面量类型
如果你需要确保CATEGORIES符合GroupByCategoryAndTrigger接口的结构,可以先让TypeScript推导常量的字面量类型,再做类型断言:
// 先定义接口 export interface GroupByCategoryAndTrigger { [key: string]: { label: string; name: string; triggers: { [key: string]: { name: string, label: string }; } } } // 先定义原始常量,用as const锁定字面量类型 const rawCategories = { reservation: { name: 'Reservation' as const, label: 'Reservation', triggers: { created: { name: 'created', label: 'Reservation created' }, confirmation: { name: 'confirmation' as const, label: 'Reservation confirmed', }, edited: { name: 'edited' as const, label: 'Reservation edited', }, canceled: { name: 'canceled' as const, label: 'Reservation cancelled', }, reminder1: { name: 'reminder_1' as const, label: 'Reminder 1', }, reminder2: { name: 'reminder_2' as const, label: 'Reminder 2', }, reminder3: { name: 'reminder_3' as const, label: 'Reminder 3', }, payment: { name: 'payment' as const, label: 'Awaiting Payment', }, feedbackRequest: { name: 'feedback_request' as const, label: 'Feedback request', }, }, }, queue: { name: 'Offline Waitlist' as const, label: 'Queue', triggers: { waitlistConfirmation: { name: 'waitlist_confirmation' as const, label: 'Waitlist confirmation', }, tableConfirmation: { name: 'table_confirmation' as const, label: 'Table confirmation' }, }, }, } as const; // 断言为接口类型,确保结构符合要求 export const CATEGORIES: GroupByCategoryAndTrigger = rawCategories; // 基于rawCategories的推导类型提取联合类型 export type CategoriesValues = typeof rawCategories[keyof typeof rawCategories]['name']; export type TriggerValues = typeof rawCategories[keyof typeof rawCategories]['triggers'][keyof typeof rawCategories[keyof typeof rawCategories]['triggers']]['name'];
这样既保留了接口的类型约束,又能提取到准确的字面量联合类型。
简化触发器类型提取(可选)
如果觉得触发器类型的写法太冗长,可以定义一个辅助类型来简化:
type CategoryTriggers<T> = T[keyof T]['triggers']; export type TriggerValues = CategoryTriggers<typeof rawCategories>[keyof CategoryTriggers<typeof rawCategories>]['name'];
内容的提问来源于stack exchange,提问作者Murtaza Hussain
相关产品推荐
相关产品推荐

