Как использовать /define Enums с проверкой типа потока?
Я пытаюсь перенести существующую базу кода, чтобы использовать 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) {
}
Теперь, если я хочу использовать типы потоков, я не могу сделать ничего подобного:
type ActiveLoanStatus =
"current" |
"due" |
"overdue" |
"pending_payment"
if (loan.status isTypeOf ActiveLoanStatus) {
}
Итак, как я должен использовать эти статические перечисления? Я должен делать это неправильно!
Ответы
Ответ 1
Вот наиболее краткий способ достижения этого:
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]) {
}
Ответ 2
В то время как невероятно многословный и немасштабируемый, он попадает в поток " Disjoint Unions" и может быть реализован с помощью ===
, Как они упоминают на этой странице, Case Analysis выполняется с помощью этого оператора, поскольку javascript, естественно, делает с операторами switch-case
.
В вашем случае это эквивалентно:
switch(loan.status) {
'pending':
'current':
'due':
'overdue':
'pending_payment':
'charged_off':
'voided':
'disputed':
'refunded':
'settled':
// your behavior here
}
Как я уже упоминал, это очень многословный код, который использует ваши типы, но для того, чтобы противостоять этому, он имеет преимущество в определении ваших типов без создания шаблонного объекта - вы просто определяете свои литературные параметры и объединяете их вместе (вторая реализация).
Это имеет очевидный недостаток в сочетании определения вашего типа с вашими реализациями его потребителей, поэтому используйте его с осторожностью.