Convert enum to string literal type union.
即将 enum 的值转换成其值构成的 union。
背景
给某项目的老代码重构增加 enum 类型,导致原本正常的字符串赋值会报错:
enum UserTypeEnum {
Admin = 'admin',
Editor = 'editor',
Reader = 'reader',
Anonymous = 'anonymous'
}
// 老代码
// let userType = 'admin';
// 新代码
let userType: UserTypeEnum = 'admin';
// ❌ Type '"admin"' is not assignable to type 'UserTypeEnum'.(2322)
if (foo && bar) {
userType = 'editor'
} else if (foz) {
userType = 'reader'
} else {
...
}
有一种解决办法是:
let userType = UserTypeEnum.Admin;
如果是新代码当然推荐如此修改,但我们只是重构修改需尽量少,故不想改字符串,而且会改动很多地方,记住我们只是想增加类型不想修改运行时代码否则可能会引入风险。一种解决办法是利用类型强制转换。
let userType = 'admin' as UserTypeEnum.Admin;
运行时代码没有变化但强制转换本身意味着不安全也不优雅,而且每一处老代码都需要增加转换代码。
要是能获取到 UserTypeEnum的值的 string literal 联合类型,就能解决问题了。也就是问题转换成:
输入
enum UserTypeEnum {
Admin = 'admin',
Editor = 'editor',
Reader = 'reader',
Anonymous = 'anonymous'
}
输出
type IUserType = 'admin' | 'editor' | 'reader' | 'anonymous';
答案
type IUserType = `${UserTypeEnum}`
简单程度是不是超出了你的预期 😄。
使用这种办法我们的代码只需要修改一行:
enum UserTypeEnum {
Admin = 'admin',
Editor = 'editor',
Reader = 'reader',
Anonymous = 'anonymous'
}
type IUserType = `${UserTypeEnum}`
- let userType = 'admin';
+ let userType: IUserType = 'admin' // 只需改一处
if (foo && bar) {
userType = 'editor'
} else if (foz) {
userType = 'reader'
} else {
...
}
原理
模板字符串会穷举目标类型的所有可能,并用字符串表示,而枚举的类型字符串表示即对应的成员的字符串字面量。
扩展
如何获取 key 构成的联合类型
type UserTypeKeys = `${keyof typeof UserTypeEnum}`;
注意 UserTypeEnum 是对象,故需先使用 typeof。
参考
stackoverflow.com/questions/5…