【问题标题】:How to use/define Enums with Flow type checking?如何使用/定义带有流类型检查的枚举?
【发布时间】:2017-01-02 10:50:45
【问题描述】:

我正在尝试迁移现有代码库以使用 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) {

}

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

【问题讨论】:

标签: javascript enums flowtype


【解决方案1】:

要使用流表达枚举,您可以将$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版本开始。

【讨论】:

  • 来自实用程序类型文档的链接:“$Values&lt;T&gt; 表示可枚举属性的所有值类型(不是值,而是它们的类型!)的联合类型。”因此,在您的示例中,LoanStatus 类型是 string,而不是 'pending' | 'current' | ...
  • @Andrew Patton 这里的技巧是使用冻结对象,然后将值类型解析为文字类型。试一试。文档中没有提到这部分(与其他许多部分一样)
  • 很高兴知道,感谢您的进一步解释!太糟糕了,文档太不完整了。我确实注意到它只适用于冻结对象加上字符串文字值。如果任何值是变量(甚至是常量),则类型变为string。例如:const PENDING = 'pending'; export const LOAN_STATUS = Object.freeze({ PENDING, DUE: 'due' });
  • @Andrew Patton 是的,那是因为在流程中(不像在打字稿中)consts 不会推断文字类型,而是扩大类型。要解决此问题,应明确指定类型(例如const PENDING: 'pending' = 'pending';
  • 哦,是的,非常感谢大家。小注意不幸的是它不适用于嵌套对象。我试图制作类似javascript const TRACK_EVENTS = Object.freeze({ homepage: { header: 'header_passed', hero: 'hero_passed', } }) tracker = (eventName: $Values&lt;typeof TRACK_EVENTS&gt;) 的东西,这似乎不起作用。
【解决方案2】:

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

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]) {

}

【讨论】:

  • 嗯。如果键不等于值...?
  • 不幸的是,在这种情况下你必须复制
【解决方案3】:

虽然非常冗长且不可扩展,但这属于 Flow 的“Disjoint Unions”案例,可以使用 === 来实现。正如他们在该页面上提到的那样,案例分析是通过该运算符完成的,就像 javascript 自然对 switch-case 语句所做的那样。

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

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

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

这有一个明显的缺点,就是将你的类型定义与其消费者的实现结合起来,所以要谨慎使用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-03-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-10
    • 1970-01-01
    • 1970-01-01
    • 2016-09-08
    相关资源
    最近更新 更多