如何使用/定义具有流类型检查的枚举?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何使用/定义具有流类型检查的枚举?相关的知识,希望对你有一定的参考价值。

我正在尝试迁移现有的代码库以使用Flow。由于这个项目在没有Flow的情况下开始,我使用了一个非常典型的JS模式用于枚举等。

以下是我想要的一些定义

export const LOAN_STATUS  = {
  PENDING: 'pending',
  CURRENT: 'current',
  DUE: 'due',
  OVERDUE: 'overdue',
  PENDING_PAYMENT: 'pending_payment',
  CHARGED_OFF: 'charged_off',
  VOIDED: 'voided',
  DISPUTED: 'disputed',
  REFUNDED: 'refunded',
  SETTLED: 'settled',
}

export const ACTIVE_LOAN_STATUS = [
  LOAN_STATUS.OVERDUE,
  LOAN_STATUS.CURRENT,
  LOAN_STATUS.DUE,
  LOAN_STATUS.PENDING_PAYMENT,
]

Flow工作正常,直到我导入此文件,它说我需要添加类型注释。这看起来很奇怪 - 为什么我必须注释完全静态且易于推断的对象?

有没有办法将其类型定义为“静态”或“文字”?

那么我就开始考虑如何为此添加注释。我的第一个念头就是{[key: string]: string}Array<string>。 Flow有效,但我意识到这些类型定义完全没用。那么我尝试另一种方法:

type LoanStatusValues =
  'pending' |
  'current' |
  'due' |
  'overdue' |
  'pending_payment' |
  'charged_off' |
  'voided' |
  'disputed' |
  'refunded' |
  'settled'

type LoanStatusKeys =
  'PENDING' |
  'CURRENT' |
  'DUE' |
  'OVERDUE' |
  'PENDING_PAYMENT' |
  'CHARGED_OFF' |
  'VOIDED' |
  'DISPUTED' |
  'REFUNDED' |
  'SETTLED'

type ActiveLoanStatus = 
"current" |
"due" |
"overdue" |
"pending_payment"

我使用类型注释{[key: LoanStatusKeys]: LoanStatusValues}Array<ActiveLoanStatus>。但即便是这些注释也忽略了这是静态的事实!

看起来很奇怪,我不得不写这么多重复的代码。然后,如果我想转换为Flow,我实际上无法使用JS中的类型。例如,我可能这样做:

if (defs.ACTIVE_LOAN_STATUS.indexOf(loan.status) !== -1) {

}

现在,如果我想使用Flow类型,我不能做这样的事情:

type ActiveLoanStatus = 
  "current" |
  "due" |
  "overdue" |
  "pending_payment"

if (loan.status isTypeOf ActiveLoanStatus) {

}

那么我该如何使用这些静态枚举?我一定是做错了!

答案

以下是实现此目的的最简洁方法:

const activeLoanStatuses = {
  current: 'current',
  due: 'due',
  overdue: 'overdue',
  pending_payment: 'pending_payment'
};

const otherLoanStatuses = {
  pending: 'pending',
  charged_off: 'charged_off',
  voided: 'voided',
  disputed: 'disputed',
  refunded: 'refunded',
  settled: 'settled',
};

type ActiveLoanStatus = $Keys<typeof activeLoanStatuses>;
type LoanStatus = $Keys<typeof otherLoanStatuses> | ActiveLoanStatus;

const activeLoanStatusesMap: { [key: LoanStatus]: ?ActiveLoanStatus} = activeLoanStatuses;

if (activeLoanStatusesMap[loan.status]) {

}
另一答案

要使用流表达枚举,可以将$Values实用程序与冻结对象类型结合使用:

export const LOAN_STATUS = Object.freeze({
  PENDING: 'pending',
  CURRENT: 'current',
  DUE: 'due',
  OVERDUE: 'overdue',
  PENDING_PAYMENT: 'pending_payment',
  CHARGED_OFF: 'charged_off',
  VOIDED: 'voided',
  DISPUTED: 'disputed',
  REFUNDED: 'refunded',
  SETTLED: 'settled',
});

type LoanStatus = $Values<typeof LOAN_STATUS>;

export const ACTIVE_LOAN_STATUS: LoanStatus[] = [
  LOAN_STATUS.OVERDUE,
  LOAN_STATUS.CURRENT,
  LOAN_STATUS.DUE,
  LOAN_STATUS.PENDING_PAYMENT,
]

这可以从0.60.0版本开始。

另一答案

虽然令人难以置信的冗长,不可扩展,但这属于Flow的“Disjoint Unions”案例,可以使用===实现。正如他们在该页面上提到的,案例分析是通过该运算符完成的,因为javascript自然地与switch-case语句一起使用。

在您的情况下,这相当于:

switch(loan.status) {
  'pending':
  'current':
  'due':
  'overdue':
  'pending_payment':
  'charged_off':
  'voided':
  'disputed':
  'refunded':
  'settled':
    // your behavior here
}

正如我所提到的,这在使用您的类型的代码中非常冗长,但为了解决这个问题,它可以在不创建样板对象的情况下定义类型 - 您只需定义文字选项并将它们组合在一起(第二个实现)。

这有一个明显的缺点,即将您的类型定义与其消费者的实现相结合,因此请谨慎使用。

以上是关于如何使用/定义具有流类型检查的枚举?的主要内容,如果未能解决你的问题,请参考以下文章

如何将具有自定义枚举类型的数据从 csv 插入现有的 PostgreSQL 表

find_if 中具有多个参数的 Lambda

如何在 TypeScript 中创建自定义类型

c#中怎样定义枚举?

具有相同功能的活动和片段

如何获取一个枚举类型元素的个数