Spec-Zone.ru › Flow

Массивы

Типы массивов представляют собой списки переменной длины, все элементы которых имеют одинаковый тип.

const arr: Array<number> = [1, 2, 3];
Сравнение с TypeScript

Сокращённый синтаксис readonly T[] из TypeScript не поддерживается в Flow; вместо него используйте ReadonlyArray<T> (TS поддерживает оба варианта). Изменяемый Array<T> также инвариантен в Flow, но ковариантен в TS, поэтому Flow может блокировать небезопасное расширение типов, которое TS допускает.

Когда это использовать​

Используйте массивы, если у вас есть список значений переменной длины, все элементы которого имеют один и тот же тип. Если список содержит фиксированное количество элементов, и для каждой позиции задан определённый тип, используйте вместо этого кортежи.

Array Type​

Тип Array<T> представляет собой массив элементов типа T. Например, массив чисел будет иметь тип Array<number>:

const arr: Array<number> = [1, 2, 3];

В Array<T> можно поместить любой тип:

const arr1: Array<boolean> = [true, false, true];
const arr2: Array<string> = ["A", "B", "C"];
const arr3: Array<unknown> = [1, true, "three"];

ReadonlyArray<T>​

Вместо Array<T> можно использовать тип ReadonlyArray<T>, обозначающий массив только для чтения, который нельзя изменять. Нельзя напрямую записывать значения в такой массив, а также использовать изменяющие его методы, например .push(), .unshift() и т. д.

const readonlyArray: ReadonlyArray<number> = [1, 2, 3]


const first = readonlyArray[0]; // OK to read
readonlyArray[1] = 20;          // Error!
readonlyArray.push(4);          // Error!
readonlyArray.unshift(4);       // Error!

Обратите внимание: массив типа ReadonlyArray<T> всё ещё может содержать изменяемые элементы:

const readonlyArray: ReadonlyArray<{x: number}> = [{x: 1}];
readonlyArray[0] = {x: 42}; // Error!
readonlyArray[0].x = 42; // Works

Основное преимущество использования ReadonlyArray вместо Array состоит в том, что параметр типа у ReadonlyArray ковариантен, а параметр типа у Array инвариантен. Это означает, что ReadonlyArray<number> является подтипом ReadonlyArray<number | string>, тогда как Array<number> НЕ является подтипом Array<number | string>. Поэтому в аннотациях типов массивов с элементами разных типов часто полезно использовать ReadonlyArray. Рассмотрим, например, следующую ситуацию:

const someOperation = (arr: Array<number | string>) => {
  // Here we could do `arr.push('a string')`
}


const array: Array<number> = [1];
someOperation(array) // Error!

Поскольку параметр arr функции someOperation имеет тип изменяемого Array, внутри этой области видимости в него можно добавить строку. Это нарушит контракт типа внешней переменной array. Если в этом случае аннотировать параметр как ReadonlyArray, Flow сможет гарантировать, что этого не произойдёт, и ошибок не будет:

const someOperation = (arr: ReadonlyArray<number | string>) => {
  // Nothing can be added to `arr`
}


const array: Array<number> = [1];
someOperation(array); // Works

ReadonlyArray<unknown> представляет собой супертype всех массивов и всех кортежей:

const tup: [number, string] = [1, 'hi'];
const arr: Array<number> = [1, 2];


function f(xs: ReadonlyArray<unknown>) { /* ... */ }


f(tup); // Works
f(arr); // Works

Литералы пустых массивов​

При проверке требований к аннотациям Flow обрабатывает литералы пустых массивов ([]) особым образом. Они особенные, потому что не содержат достаточно информации для определения типа и при этом встречаются слишком часто, чтобы всегда требовать аннотацию типа непосредственно в месте их использования.

Поэтому при определении типа пустых массивов Flow следует таким правилам:

Контекстный вывод типов​

Во-первых, если доступна контекстная информация, Flow использует её для определения типа элементов массива:

function takesStringArray(x: Array<string>): void {}


const arr1: Array<string> = [];
takesStringArray([]);

В обоих случаях для [] будет выведен тип Array<string>.

Обратите внимание: чтобы контекстная информация сработала, тип должен быть доступен непосредственно в месте определения массива. Это означает, что последние две строки следующего кода вызовут ошибки:

function takesStringArray(x: Array<string>): void {}


const arr2 = []; // Error
takesStringArray(arr2); // Error

Вторая ошибка возникает из-за того, что для arr2 выводится тип Array<empty>, что приводит к ещё одной ошибке при вызове takesStringArray.

Вывод типов по инициализатору​

Flow позволяет определять типы пустых массивов ещё одним способом, если они сразу присваиваются переменной:

const arr3 = [];

Этот подход напоминает проверку типов переменных без инициализаторов: Flow пытается определить тип переменной по её «первому» присваиванию или присваиваниям. Для пустых массивов присваиванием считается

  • оператор индексированной записи a[i] = e; или
  • вызов метода массива push a.push(e).

В обоих случаях тип e используется как тип элемента массива.

Вот несколько примеров:

Последовательный код​

После обнаружения первого присваивания тип элемента массива фиксируется в соответствии с типом присвоенного выражения. Последующие записи элементов другого типа приводят к ошибкам:

const arr3 = [];
arr3.push(42); // arr3 is inferred as Array<number>
arr3.push("abc"); // Error!

Условный код​

Если массиву присваиваются значения в соседних ветвях условных операторов, тип его элементов фиксируется как объединение присвоенных типов:

declare const cond: boolean;


const arr4 = [];
if (cond) {
  arr4[0] = 42;
} else {
  arr4.push("abc");
}
// arr4 is inferred as Array<number | string>
arr4.push("def"); // Works
arr4[0] = true; // Error!

Побеждает ближайшая область видимости​

Если присваивания выполняются в нескольких областях видимости, предпочтение отдаётся области, в которой присваивание выполняется на более поверхностном уровне:

const arr5 = [];
function f() {
  arr5.push(42); // Error!
}
f();
arr5.push("abc"); // This assignment wins. arr5 is inferred as Array<string>
arr5.push(1); // Error!

Доступ к массиву может быть небезопасным​

По умолчанию при обращении к элементу массива возвращается тип элемента T, хотя во время выполнения значение может оказаться undefined. Например, это происходит при чтении индекса за пределами массива:

const array: Array<number> = [0, 1, 2];
const value: number = array[3]; // Works
                         // ^ undefined

То же происходит, если массив «разреженный» и элементу никогда не присваивалось значение:

const array: Array<number> = [];


array[0] = 0;
array[2] = 2;


const value: number = array[1]; // Works
                         // ^ undefined

В обоих случаях Flow принимает код и определяет для value тип number, хотя во время выполнения значение равно undefined.

Чтобы обнаружить такие случаи, включите параметр no_unchecked_indexed_access. При его включении при обращении к элементам любого массива (и словаря) возвращается T | void вместо T, поэтому перед использованием значения Flow требует обработать случай undefined:

const array: Array<number> = [0, 1, 2];
const value: number | void = array[1];


if (value !== undefined) {
  // number
}

Нерекомендуемый сокращённый синтаксис типа массива​

Вместо синтаксиса Array<T> можно использовать T[]. Этот синтаксис не рекомендуется и в будущем может быть объявлен устаревшим.

const arr: number[] = [0, 1, 2, 3];

Обратите внимание: ?Type[] эквивалентно ?Array<T>, а не Array<?T>.

const arr1: ?number[] = null;   // Works
const arr2: ?number[] = [1, 2]; // Works
const arr3: ?number[] = [null]; // Error!

Чтобы сделать его Array<?T>, можно использовать скобки, например: (?Type)[]

const arr1: (?number)[] = null;   // Error!
const arr2: (?number)[] = [1, 2]; // Works
const arr3: (?number)[] = [null]; // Works

См. также​

  • Кортежи — коллекции фиксированной длины, в которых каждый элемент может иметь свой тип
  • Вариантность — почему ReadonlyArray ковариантен, а Array инвариантен
  • Объекты — ещё один базовый тип структуры данных в Flow

© 2013–present Facebook Inc.
Licensed under the MIT License.
https://flow.org/en/docs/types/arrays

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API