TypeScript通用枚举成员校验函数的类型收窄问题
TypeScript通用枚举校验函数的类型收窄解决方案
需求是实现一个通用函数,接收枚举类型与变量,校验变量是否为该枚举的成员,且校验通过后自动收窄变量类型。现有两种实现方案均存在问题:
现有方案的问题
第一种方案定义:
export function isEnumMember<T extends Record<string, K>, K>(value: unknown, enumType: T): value is K问题:校验通过后变量类型未被正确收窄,仍保持原类型(如
unknown、string),因为TypeScript无法从T extends Record<string, K>的约束中准确推断出K为枚举的成员值类型。第二种方案将返回类型改为
value is T:
问题:在字符串枚举场景下,变量类型会变为string & typeof WORK_TYPE_ENUM这类交叉类型,无法直接适配prefix_${WORK_TYPE_ENUM}这类模板字符串的赋值需求,必须手动使用as断言才能正常工作。
正确的通用实现
export function isEnumMember<T extends Record<string, string | number>>(value: unknown, enumType: T): value is T[keyof T] { // 过滤数字枚举的反向映射键(只保留值为字符串或数字的枚举成员) const enumValues = Object.values(enumType).filter(v => typeof v === 'string' || typeof v === 'number') as T[keyof T][]; return enumValues.includes(value as T[keyof T]); }
方案说明
- 类型推断:
T[keyof T]准确获取枚举的成员值类型(无论是字符串枚举还是数字枚举),校验通过后变量会被正确收窄为该类型。 - 兼容数字枚举:通过
filter过滤掉数字枚举自动生成的反向映射键(数字键对应的字符串值),避免误判。 - 模板字符串适配:字符串枚举场景下,收窄后的类型为枚举成员的字符串字面量类型,可直接用于模板字符串拼接,无需额外断言。
使用示例
// 字符串枚举示例 enum WORK_TYPE_ENUM { FULL_TIME = "full-time", PART_TIME = "part-time" } function checkWorkType(val: unknown) { if (isEnumMember(val, WORK_TYPE_ENUM)) { // val类型被收窄为WORK_TYPE_ENUM,可直接用于模板字符串 const labeledType = `work_type_${val}`; console.log(labeledType); // 类型为`"work_type_full-time" | "work_type_part-time"` } } // 数字枚举示例 enum STATUS { ACTIVE = 1, INACTIVE = 0 } function checkStatus(val: unknown) { if (isEnumMember(val, STATUS)) { // val类型被收窄为STATUS const status: STATUS = val; console.log(status); // 类型为STATUS } }
内容的提问来源于stack exchange,提问作者A-S
相关产品推荐
相关产品推荐

