likes
comments
collection
share

从JS到TS的平滑过渡-用常量实现枚举效果

作者站长头像
站长
· 阅读数 88

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
评论
请登录