Общий шаблон в JS при работе с предварительно определенным набором строковых значений заключается в том, чтобы приложить их в объект и использовать его в качестве карты.
Скажите, что мы имеем дело с субъектом – допустим, просьба присоединиться к группе – это может быть в 3 и только 3 возможных состояниях:
одобренныйотклоненныйв ожидании
Мы могли бы использовать эти значения прямо в код, как они есть:
if (request.status === 'approved') {
// ...
}
// elsewhere in the code
request.status === 'approved' ? showPage() : null
Хотя это работает, это склонно к ошибкам, так как мы упускаем завершение в нашей IDE (это просто строка), Плюс это действительно не будущее. Если наш бэкэнд изменит строку для одобрения, чтобы быть Подтверждено Или даже просто Одобренный Нам внезапно придется смягчить нашу кодовую базу, чтобы изменить все экземпляры утверждено (Надеясь, что та же самая строка не используется в другом месте в совершенно не связанных контекстах, которые непреднамеренно изменяются нашим Поиск и заменить).
Первое средство, как правило, заключается в том, чтобы объединить эту строку в объект и экспортировать ее в качестве карты.
export const statuses = {
approved: 'approved',
rejected: 'rejected',
pending: 'pending'
};
Теперь мы можем реорганироваться наш код:
import { statuses } from '../../statuses.js';
if (request.status === statuses.approved) {
// ...
}
// ...
request.status === statuses.approved ? showPage() : null
В первую очередь мы получаем приятное разрешение при вводе кода, плюс мы уверены, что Имел API слегка изменять и поменял строку утверждено с Одобренный Нам нужно изменить Одна линия против столько же, сколько мы использовали это значение.
Если вы используете проверку типа в JS, у вас также будет еще лучшая гарантия при использовании этих статусов. Естественным способом предоставления определения типа в этом случае будет перечисление.
Чтобы выразить это в потоке, мы использовали бы союз, и первая попытка, возможно, будет следующим.
/* @flow */
const statuses = {
approved: 'approved',
rejected: 'rejected',
pending: 'pending'
};
type Status = 'approved' | 'rejected' | 'pending';
Это абсолютно работает, и в этом нет ничего плохого. Мы можем проверить переписку потока:
/* @flow */
const statuses = {
approved: 'approved',
rejected: 'rejected',
pending: 'pending'
};
type Status = 'approved' | 'rejected' | 'pending';
const getStatus = (obj: { status: Status }): Status => obj.status;
// this will pass, all good
const ret0 = getStatus({ status: statuses.approved});
// this will throw an error, again, all good
const ret1 = getStatus({ status: 'nope' }); // ERROR: Cannot call `getStatus` with object literal bound to `obj` because string [1] is incompatible with enum [2] in property `status`.
Вы можете попробовать это Здесь Анкет
Хотя это работает, мы должны заметить повторение, так как мы вручную транскрибируем все значения объекта в Статус тип. Это все еще может быть в порядке, поскольку мы просто используем 3 значения, но что, если значения должны были увеличиться до 10? Или до 20? Поддержание определения типа станет утомительным и подверженным ошибкам.
Решение состоит в том, чтобы использовать $ Значения и тип Чтобы извлечь значения:
/* @flow */
const statuses = {
approved: 'approved',
rejected: 'rejected',
pending: 'pending'
};
type Status = $Values;
Если мы попробуем это, мы увидим странное поведение:
// this will pass, all good
const ret0 = getStatus({ status: statuses.approved});
// this will pass!! That's not good!
const ret1 = getStatus({ status: 'nope' });
Вы можете попробовать это Здесь Анкет
Если мы спросим нашу IDE о возвращении типа нашего GetStatus Функция мы увидим, что это на самом деле нить а не Один из членов нашего Статус тип союза.
Причина заключается в том, что когда мы создаем объект с От документов (акцент мой):
Когда вы создаете объект с его свойствами, вы создаете тип герметичного объекта в потоке. Эти запечатанные объекты будут знать все свойства, с которыми вы их объявили, и Типы их значений Анкет
Также:
Но когда объекты запечатаны, поток не позволит вам Добавить новые свойства им.
Прежде всего, давайте проясним, что такое герметичный объект. От Mdn :
Метод object.seal () запечатывает объект, предотвращая добавление новых свойств к нему и маркировку всех существующих свойств как неконфигурируемые. Значения нынешних свойств все еще могут быть изменены до тех пор, пока они доступны для записи.
Это означает, что мы не сможем добавить новые ключи к этому объекту (он бросит ошибку), но мы сможем изменить существующие значения. В этом случае поток сможет вывести только тип значений, но не их буквальные значения, поскольку они все еще будут доступны для изменения позже в коде.
Хитрость, чтобы сделать поток, действительно извлечь эти буквальные значения в перечисление, состоит в том, чтобы заморозить объект так:
const statuses = Object.freeze({
approved: 'approved',
rejected: 'rejected',
pending: 'pending'
});
Что Object.freeze Сделаю делать все, что Object.Seal Плюс убедитесь, что пара ключей/значения не может быть изменена.
Снова Mdn :
Метод Object.Freeze () замораживает объект. Замороженный объект больше не может быть изменен; Замораживание объекта предотвращает добавление новых свойств к нему, существующие свойства удаляются, предотвращает изменение перечисления, конфигурации или записи существующих свойств и предотвращает изменение значений существующих свойств. Кроме того, замораживание объекта также предотвращает изменение его прототипа. Freeze () возвращает тот же объект, который был передан.
Наконец, теперь поток сможет правильно извлечь значения (а не только их примитивные типы). Давайте проверим это:
const statuses = Object.freeze({
approved: 'approved',
rejected: 'rejected',
pending: 'pending'
});
type Status = $Values;
const getStatus = (obj: { status: Status }): Status => obj.status;
// this will pass, all good
const ret0 = getStatus({ status: statuses.approved});
// this will throw an error again, exactly as we want
const ret1 = getStatus({ status: 'nope' }); // ^ Cannot call `getStatus` with object literal bound to `obj` because string [1] is incompatible with enum [2] in property `status`.
Теперь мы уверены, что можем расширить Статусы Чтобы поддержать больше ключевых/значений без дублирования значений в нашем типе объединения.
Рекомендации
- Поток документов на Вывод типа объекта
- Еще одно ясное и короткое объяснение Object.Freeze vs Object.Seal
- Удивительное изображение обложки – Джан-Рето Тарнутцер
Первоначально опубликовано на Мой блог Анкет
Оригинал: “https://dev.to/aurelio/how-to-to-derive-enum-union-values-in-flow-without-duplication-3cb2”