Flow - это средство проверки типов, созданное Facebook для проверки типов данных JavaScript. Он имеет множество встроенных типов данных, которые мы можем использовать для аннотирования типов переменных и параметров функций.

В этой статье мы рассмотрим некоторые типы встроенных утилит, которые поставляются с Flow.

$ Ключи ‹T›

Тип $Keys<T> получает свойства от типа и использует его как свой собственный тип. Это также полезно для создания перечислений.

Например, мы можем написать:

const fruits = {
  apple: 'Apple',
  orange: 'Orange',
  banana: 'Banana'
};
type Fruit = $Keys<typeof fruits>;
let a: Fruit = 'apple';

Приведенный выше код определит новый тип Fruit с объединением ключей объекта fruits. Это означает, что с переменной типа Fruit мы можем присвоить ей 'apple', 'orange' или 'banana'.

Назначение чего-либо еще должно вызвать ошибку:

let b: Fruit = 'grape';

$ Values ​​‹T›

Тип $Values<T> позволяет нам получать типы типа объекта и создавать тип из их объединения.

Например, мы можем написать:

type Person = {
  name: string,
  age: number,
};
type PersonValues = $Values<Person>;
let a: PersonValues = 1;
let b: PersonValues = 'Joe';

В коде мы определили тип Person с некоторыми свойствами, а затем $Values<Person> берет типы name и age и создает из них объединение. Итак, тип PeronValues string | number.

$ ReadOnly ‹T›

$ReadOnly<T> дает нам доступную только для чтения версию типа T.

Следующий:

type ReadOnlyObj = {
  +foo: string
};

а также:

type ReadOnlyObj = $ReadOnly<{
  foo: string
}>;

эквивалентны.

Это удобно, когда мы хотим переопределить все свойства типа объекта как доступные только для чтения.

Например, мы можем написать следующее, чтобы определить тип и объект только для чтения:

type Person  = {
  name: string,
  age: number,
};
type ReadOnlyPerson = $ReadOnly<Person>;
let person: ReadOnlyPerson = {
  name: 'Joe',
  age: 10
}

Затем, когда мы пытаемся переназначить свойство в объекте person следующим образом:

person.name = 'Bob';

Мы получим ошибку.

$ Exact ‹T›

$Exact<T> позвольте нам создать точный тип из типа объекта.

Это означает, что:

type ExactPerson = $Exact<{name: string}>;

такой же как:

type ExactPerson = {| name: string |};

$ Diff ‹A, B›

$Diff<A, B> создает тип со свойствами, которые есть в A, но не в B, где оба являются типами объектов. Это то же самое, что и A \ B, что представляет собой установленную разницу между A и B.

Например, если у нас есть 2 типа:

type Person  = {
  name: string,
  age: number,
};
type Age = { age: number };

Затем мы можем создать тип объекта, который принимает свойство name, с помощью:

type Name = $Diff<Person, Age>;

Когда мы создаем объект с типом Name, нам требуется свойство name:

let name: Name = { name: 'Mary' };

Если свойство name не включено в объект:

let foo: Name = { age: 10 };

Тогда мы получим ошибку.

$ Остальное ‹A, B›

$Rest<A, B> похож на $Diff<A, B>, но проверка свойств выполняется во время выполнения. Это свойства, которые являются частью оператора rest.

В результате получаются свойства собственных свойств A, которые не являются собственными свойствами B. В Flow определенные типы объектов рассматриваются как имеющие собственные свойства.

Например, мы можем определить новый тип объекта с $Rest<A, B>, запустив:

type Person = { name: string, age: number };
const person: Person = {name: 'Jon', age: 42};
const {age, ...rest} = person;
(name: $Rest<Person, {|age: number|}>);

Тогда объект name не может больше иметь свойство age, поскольку мы привели его к типу $Rest<Person, {|age: number|}>. $Rest<Person, {|age: number|}> возвращает новый тип со свойством age, удаленным из типа Person.

$ PropertyType ‹T, k›

$PropertyType<T, k> получает тип ключа k от типа T. Ключ k - это строка. Например, если мы создадим тип Person следующим образом:

type Person = { name: string, age: number };

Тогда $PropertyType<Person, ‘name’> даст нам string тип:

let foo: $PropertyType<Person, 'name'> = 'name';

Вложенные поиски тоже работают. Например, мы можем написать:

type Person = { 
  name: {
    firstName: string,
    lastName: string
  }, 
  age: number 
};
let foo: <$PropertyType<$PropertyType<Person, 'name'>, 'firstName'> = 'name';

$ ElementType ‹T, K›

$ElementType<T, K> - это тип, который представляет каждый элемент в массиве, кортеже или типе объекта T, который соответствует имени ключа K.

Например, если у нас есть следующий тип:

type Person = { 
  name: string, 
  age: number 
};

Затем мы можем использовать его для получения типа свойства name, написав:

$ElementType<Person, 'name'>

А затем мы можем использовать его для аннотирования типа других переменных:

let foo: $ElementType<Person, 'name'> = 'name';

Мы можем использовать его с кортежами следующим образом:

type Tuple = [boolean, string];
let foo: $ElementType<Tuple, 0> = true;

С динамическими типами объектов мы можем передать тип ключа непосредственно второму аргументу. Например, если у нас есть следующий тип:

type DynamicObj = { [key: string]: number };

Затем мы можем получить тип ключей свойств с $ElementType, написав:

let x: $ElementType<DynamicObj, string> = 1;

Для массивов мы можем написать следующее:

type StrArrayObj = {
  strings: Array<string>,
};
let x: $ElementType<$ElementType<StrArrayObj, 'strings'>, number> = 'abc';

Приведенный выше код работает следующим образом. $ElementType<StrArrayObj, ‘strings’> дает нам тип свойства strings для StrArrayObj. Затем мы применяем $ElementType с $ElementType<StrArrayObj, ‘strings’> и number, чтобы получить тип элементов массива strings.

number предназначен для получения индекса массива strings.

В Flow есть много полезных типов утилит, которые упрощают создание новых типов. Мы можем получить ключи объекта для создания нового типа объединения. Также мы можем получить типы значений и создать из них новый тип объединения.

Также есть тип, позволяющий изменить все свойства на доступ только для чтения. Мы также можем получить типы ключей объекта, кортежи или записи массивов.