如何获取 enum 的值构成的类型

151 阅读1分钟

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}`

简单程度是不是超出了你的预期 😄。

image.png

使用这种办法我们的代码只需要修改一行:

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…

关联文章