从JS到TS的平滑过渡-用常量实现枚举效果
JS习惯用法
这是我们经常会用到的一种编写常量的方式
const AccountStatus = {
NONACTIVE: 0,
ACTIVE: 1,
DISABLE: 2,
DELETED: 3,
};
一般约束场景
TS类型推断结果为
const AccountStatus: {
NONACTIVE: number;
ACTIVE: number;
DISABLE: number;
DELETED: number;
};
应用场景
// 取值 const activeAccountStatus: number
const activeAccountStatus = AccountStatus.ACTIVE;
// 赋值给number类型的值
let accountStatus = 0;
accountStatus = AccountStatus.DISABLE;
// 取key或者value const accountStatusEntries: [string, number][]
const accountStatusEntries = Object.entries(AccountStatus);
// 在函数参数需要status为number的时候
function judgeAccountStatus(status: number) {
switch (status) {
case AccountStatus.NONACTIVE:
console.log(status); // status: number
break;
case AccountStatus.ACTIVE:
console.log(status); // status: number
break;
case AccountStatus.DISABLE:
break;
case AccountStatus.DELETED:
break;
default:
break;
}
}
judgeAccountStatus(AccountStatus.DISABLE)
如果只需控制在像number这种基本类型的话,无需修改便可取到常量的类型
取得对象值的类型
如上的 judgeAccountStatus 函数中我们status 的类型是手动根据我们自己的判断去定义的,如果AccountStatus的值有一个变为了string类型,我们的项目中确实有这样number和string共存的常量对象
const AccountStatus = {
NONACTIVE: 0,
ACTIVE: 1,
DISABLE: 2,
DELETED: '3',
};
我们还需要手动更改judgeAccountStatus的类型定义,而且有时候可能忘记了修改
function judgeAccountStatus(status: string | number) {
那我们就需要取得AccountStatus对象的所有值的类型,将函数定义为:
function judgeAccountStatus(status: AccountStatusTypeValues) {
实现
// 获取每一项的类型的联合类型,可提取到公共types里
type ConstantsObjectTypeValues<
T extends Record<string | number | symbol, unknown>,
> = T[keyof T];
// string | number
type AccountStatusTypeValues = ConstantsObjectTypeValues<typeof AccountStatus>;
我们可以获取到AccountStatusTypeValues的类型为string | number了,对于正常的来说
const AccountStatus = {
NONACTIVE: 0,
ACTIVE: 1,
DISABLE: 2,
DELETED: 3,
};
// number
type AccountStatusTypeValues = ConstantsObjectTypeValues<typeof AccountStatus>;
我们可以获取到AccountStatusTypeValues的类型为number
无论AccountStatus的值类型怎么变,我们都可以取到,在函数judgeAccountStatus中,我们就不用再去手动更改类型了
实现枚举约束的场景
在judgeAccountStatus函数中,我们给status添加了类型定义,并且能自动推断AccountStatus值的类型,但是推断出来的都是基本类型,我们可以像下边这样调用
judgeAccountStatus(AccountStatus.DISABLE)
judgeAccountStatus(9527)
如果我们想要控制 AccountStatusTypeValues 的类型为 AccountStatus的取值的字面量,而不仅仅是number
// status: 0 | 1| 2| 3
function judgeAccountStatus(status: AccountStatusTypeValues) {
加强类型约束
const AccountStatus = {
NONACTIVE: 0,
ACTIVE: 1,
DISABLE: 2,
DELETED: 3,
} as const;
在对象后边添加了 as const
TS类型推断为
const AccountStatus: {
readonly NONACTIVE: 0;
readonly ACTIVE: 1;
readonly DISABLE: 2;
readonly DELETED: 3;
};
每个值的类型都变为了只读的字面量类型
值的类型为
// 0 | 1 | 2 | 3
type AccountStatusTypeValues = ConstantsObjectTypeValues<typeof AccountStatus>;
这样就达到了我们把它作为枚举类型的约束,再去传AccountStatus值之外的值,编译器就会给我们错误提示了
// ⛔️ Argument of type '9527' is not assignable to parameter of type 'AccountStatusTypeValues'.ts(2345)
judgeAccountStatus(9527);
我们只在常量对象后添加了一个as const,就可以实现常量枚举的约束,无需修改原先的常量写法
建议
灵活选择约束程度
- 我们可以自行控制类型约束的粗细程度,既可以只做基本类型的约束,也可以做枚举类型的约束,这个取决于具体的场景、对TS的熟悉程度、对健壮程度的取舍,没有绝对的,只要它能帮助你减少错误就够了
防御性代码
- 像上边的对常量的值进行switch的过程中,尽管我们能做到枚举类型程度的约束,但是像服务端传过来的枚举值,类型都是我们通过 as 赋予它的,我们不能保证接口的质量,再着,在JS和TS共存的项目中,我们不能保证JS代码调用TS代码的参数类型,所以像这个函数对default的处理还是必要的
转载自:https://juejin.cn/post/7135069021820420132