Типы объединения
Иногда полезно создать тип, который является одним из набора других типов. Например, вы можете захотеть написать функцию, которая принимает набор примитивных типов значений. Для этого Flow поддерживает типы объединения.
// @flow
function toStringPrimitives(value: number | boolean | string) {
return String(value);
}
toStringPrimitives(1); // Works!
toStringPrimitives(true); // Works!
toStringPrimitives('three'); // Works!
// $ExpectError
toStringPrimitives({ prop: 'val' }); // Error!
// $ExpectError
toStringPrimitives([1, 2, 3, 4, 5]); // Error!
Синтаксис типов объединения
Типы объединения представляют собой любое количество типов, объединённых вертикальной чертой |.
Type1 | Type2 | ... | TypeN
Вы также можете добавить ведущую вертикальную черту, что полезно при разбиении типов объединения на несколько строк.
type Foo = | Type1 | Type2 | ... | TypeN
Каждый из членов типа объединения может быть любым типом, даже другим типом объединения.
type Numbers = 1 | 2; type Colors = 'red' | 'blue' type Fish = Numbers | Colors;
Если вы включили Flow Enums, они могут быть альтернативой объединениям примитивных значений.
Типы объединения требуют одного входного значения, но требуют обработки всех возможных
При вызове нашей функции, принимающей тип объединения, мы должны передать один из этих типов. Но внутри нашей функции мы обязаны обработать все возможные типы.
Давайте перепишем нашу функцию, чтобы обработать каждый тип индивидуально.
// @flow
// $ExpectError
function toStringPrimitives(value: number | boolean | string): string { // Error!
if (typeof value === 'number') {
return String(value);
} else if (typeof value === 'boolean') {
return String(value);
}
}
Вы заметите, что если мы не обработаем каждый возможный тип нашего значения, Flow выдаст ошибку.
Объединения и уточнения
Когда у вас есть значение, являющееся типом объединения, часто бывает полезно разбить его и обработать каждый отдельный тип отдельно. С типами объединения в Flow вы можете «уточнить» значение до единого типа.
Например, если у нас есть значение с типом объединения, которое является number, boolean, или string, мы можем обработать случай с числом отдельно, используя оператор JavaScript typeof.
// @flow
function toStringPrimitives(value: number | boolean | string) {
if (typeof value === 'number') {
return value.toLocaleString([], { maximumSignificantDigits: 3 }); // Works!
}
// ...
}
Проверяя typeof нашего значения и проверяя, является ли оно числом, Flow знает, что внутри этого блока оно является только числом. Затем мы можем написать код, который обрабатывает наше значение как число внутри этого блока.
Разъединённые объединения объектов
Существует особый тип объединения в Flow, известный как «разъединённое объединение объектов», который можно использовать в уточнениях. Эти разъединённые объединения объектов состоят из любого числа типов объектов, каждый из которых помечен одной собственностью.
Например, представим, что у нас есть функция для обработки ответа от сервера после отправки запроса. Если запрос успешен, мы получим объект со свойством type, установленным в 'success', и обновлённым свойством value.
{ type: 'success', value: 23 };
Если запрос завершается ошибкой, мы получим объект со свойством type, установленным в 'error', и свойством error, описывающим ошибку.
{ type: 'error', error: 'Bad request' };
Мы можем попытаться выразить оба этих объекта в одном типе объекта. Однако мы быстро столкнёмся с проблемами, когда мы знаем, что свойство существует на основе свойства type, но Flow этого не знает.
// @flow
type Response = {
type: 'success' | 'error',
value?: number,
error?: string
};
function handleResponse(response: Response) {
if (response.type === 'success') {
// $ExpectError
const value: number = response.value; // Error!
} else {
// $ExpectError
const error: string = response.error; // Error!
}
}
Попытка объединить эти два отдельных типа в один приведёт только к проблемам.
Вместо этого, если мы создадим тип объединения обоих типов объектов, Flow сможет узнать, какой объект мы используем, исходя из свойства type.
// @flow
type Response =
| { type: 'success', value: 23 }
| { type: 'error', error: string };
function handleResponse(response: Response) {
if (response.type === 'success') {
const value: number = response.value; // Works!
} else {
const error: string = response.error; // Works!
}
}
Для использования этого шаблона должно быть ключевое слово, которое присутствует во всех объектах вашего объединения (в нашем примере выше, type), и каждый объект должен установить различный литеральный тип для этого ключа (в нашем примере строка 'success', и строка 'error'). Вы можете использовать любой тип литерала, включая числа и булевы значения.
Объединения без пересечений с точными типами
Объединения без пересечений требуют использования одного свойства для различения каждого типа объекта. Вы не можете различать два разных объекта по разным свойствам.
// @flow
type Success = { success: true, value: boolean };
type Failed = { error: true, message: string };
function handleResponse(response: Success | Failed) {
if (response.success) {
// $ExpectError
var value: boolean = response.value; // Error!
}
}
Это связано с тем, что в Flow допустимо передавать значение объекта с большим количеством свойств, чем ожидается от типа объекта (из-за подтипизации ширины).
// @flow
type Success = { success: true, value: boolean };
type Failed = { error: true, message: string };
function handleResponse(response: Success | Failed) {
// ...
}
handleResponse({
success: true,
error: true,
value: true,
message: 'hi'
});
Если объекты каким-либо образом не конфликтуют друг с другом, нет способа их различить.
Однако, чтобы обойти это, вы можете использовать точные типы объектов.
// @flow
type Success = {| success: true, value: boolean |};
type Failed = {| error: true, message: string |};
type Response = Success | Failed;
function handleResponse(response: Response) {
if (response.success) {
var value: boolean = response.value;
} else {
var message: string = response.message;
}
}
С точными типами объектов дополнительные свойства недопустимы, поэтому объекты вступают в конфликт друг с другом, и мы можем различить, какой из них используется.
© 2013–present Facebook Inc.
Licensed under the MIT License.
https://flow.org/en/docs/types/unions