Массивы
Типы массивов представляют собой списки переменной длины, все элементы которых имеют одинаковый тип.
const arr: Array<number> = [1, 2, 3];
Сокращённый синтаксис 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;или - вызов метода массива
pusha.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