web-dev-qa-db-ja.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,
]

このファイルをインポートし、型注釈を追加する必要があると表示されるまで、フローは正常に機能します。これは奇妙に思われます-完全に静的で簡単に推測できるオブジェクトに注釈を付ける必要があるのはなぜですか?

タイプを「静的」または「リテラル」として定義する方法はありますか?

それで、これに注釈を追加する方法を考えます。私の最初の考えは{[key: string]: string}Array<string>だけです。フローは機能しますが、これらの型定義はまったく役に立たないことに気付いています。それで私はこの他のアプローチを試します:

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

}

フロータイプを使用する場合、次のようなことはできません。

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

if (loan.status isTypeOf ActiveLoanStatus) {

}

では、これらの静的列挙型をどのように使用するのですか?私はこれを間違っているに違いない!

21
Chet

Enumをフローで表現するには、凍結オブジェクトタイプと組み合わせて $ 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,
]

これは .60. バージョンから機能します。

13
Aleksey L.

これを実現する最も簡潔な方法は次のとおりです。

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

}
12
vkurchatkin

信じられないほど冗長でスケーラブルではありませんが、これはFlowの " Disjoint Unions "の場合に該当し、===を使用して実装できます。彼らがそのページで述べているように、javascriptがswitch-caseステートメントで自然に行うように、ケース分析はその演算子を介して行われます。

あなたの場合、それは次と同等です:

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

すでに述べたように、これは型を使用するコードでは非常に冗長ですが、これに対抗するには、ボイラープレートオブジェクトを作成せずに型を定義できるという利点があります。リテラルオプションを定義し、それらを結合するだけです(2番目の実装)。

これには、型定義とそのコンシューマの実装を結び付ける明らかな欠点があるため、注意して使用してください。

1
ABMagil