Коллекции и массивы в TypeScript
Дальше: Дженерики · Объекты и классы · Функции · Справочник — составные типы
JavaScript даёт массивы, Map, Set и обычные объекты-словари. TypeScript описывает элементы и ключи так, чтобы map, filter и доступ по индексу не превращались в any. Здесь — выбор структуры и типичные ошибки, без копирования всех таблиц из 301.
Маршрут: Типы и типизация → коллекции → дженерики → классы.
Runtime-поведение массивов и итераторов — типы в JavaScript. Циклы
for— 13.md.
Карта структур данных
| Структура | Тип TS | Когда выбирать |
|---|---|---|
| Список однотипных значений | T[] / Array<T> | порядок, индекс, map |
| Фиксированная позиция и тип | кортеж [A, B] | координаты, [data, err] |
| Словарь с известными ключами | Record<K, V> | роли, статусы, конфиг |
| Динамические ключи | Map<K, V> | ключ не только string |
| Уникальные значения | Set<T> | дедупликация, membership |
| Слабые ссылки на объекты | WeakMap, WeakSet | кэш без утечки |
Массивы: T[] и Array<T>
const tags: string[] = ["ts", "js"];
const scores: Array<number> = [10, 20, 30];
function last<T>(items: T[]): T | undefined {
return items[items.length - 1];
}
const lastTag = last(tags); // string | undefined
Разбор:
string[]иArray<string>эквивалентны для чтения.- Индекс за границей даёт
T | undefinedприnoUncheckedIndexedAccess— 6.md. - Пустой массив
[]без аннотации выводится какnever[]— укажите тип явно:const ids: string[] = [].
Readonly-массивы
const frozen: readonly number[] = [1, 2, 3];
// frozen.push(4); // ошибка
type ReadonlyNums = ReadonlyArray<number>;
Разбор:
readonlyзапрещает мутацию методовpush,sort(in-place), но не делает элементы глубоко неизменяемыми.- Для вложенных структур нужен
Readonlyна уровне полей — 10.md.
Кортежи (tuples)
Кортеж — массив фиксированной длины с типом на каждой позиции:
type Rgb = [number, number, number];
const red: Rgb = [255, 0, 0];
type HttpLine = [status: number, message: string];
const ok: HttpLine = [200, "OK"];
Labeled tuples (именованные элементы)
Метки [x: number, y: number] не попадают в runtime — это подсказки для IDE и документация позиций. Длина и типы по индексам по-прежнему проверяются жёстко:
type Point2D = [x: number, y: number];
type Point3D = [x: number, y: number, z: number];
const cursor: Point2D = [320, 240];
const vertex: Point3D = [1.0, 0.5, -1.0];
// const bad2d: Point2D = [320, 240, 0];
// ошибка: source has 3 element(s) but target allows only 2
// const bad3d: Point3D = [320, 240];
// ошибка: source has 2 element(s) but target requires 3
Разбор:
Point2Dиnumber[]— разные контракты: кортеж фиксирует длину, массив — нет.- При деструктуризации
const [x, y] = cursorтипыxиy—number; имена из типа в подсказках при наведении наcursor[0].
Опциональный элемент и rest:
type HttpResponse = [code: number, message?: string, ...headers: string[]];
const r1: HttpResponse = [404];
const r2: HttpResponse = [200, "OK", "Content-Type: text/plain"];
Разбор:
- Именованные элементы кортежа (
status:) — только для подсказок, в runtime их нет. - Rest
...headersсобирает оставшиеся элементы в массивstring[]. - Кортеж
[number, number]фиксирует длину и позиции;number[]допускает любое количество элементов.
as const и кортежи-литералы
const point = [10, 20] as const;
// readonly [10, 20] — литеральные типы
type PointTuple = typeof point;
Разбор:
as constсужает типы до литералов и добавляетreadonly.- Удобно для списков маршрутов, ключей — связка с
typeofв 10.md.
Record<K, V>
Словарь, где ключи — подмножество строк (часто union), а значения одного типа:
type Role = "admin" | "user" | "guest";
type Permissions = Record<Role, string[]>;
const perms: Permissions = {
admin: ["read", "write", "delete"],
user: ["read"],
guest: [],
};
Разбор:
- TypeScript проверит, что все ключи
Roleприсутствуют. - Лишний ключ в литерале — excess property check (как у объектов).
Индексная сигнатура для произвольных строк:
type StringDict = Record<string, number>;
const scores: StringDict = {};
scores["alice"] = 10;
Map<K, V> и Set<T>
interface User {
id: string;
name: string;
}
const byId = new Map<string, User>();
byId.set("u1", { id: "u1", name: "Ann" });
function getOrThrow(map: Map<string, User>, id: string): User {
const user = map.get(id);
if (!user) throw new Error(`User not found: ${id}`);
return user;
}
const uniqueTags = new Set<string>(["ts", "ts", "js"]);
uniqueTags.add("node");
Разбор:
Record | Map | |
|---|---|---|
| Ключи | в основном string / union | любой тип (object, number) |
| Порядок | не гарантирован для всех операций | порядок вставки |
| Размер | Object.keys | .size |
| Сериализация JSON | естественна | нужен Array.from(map) |
Set — проверка уникальности и быстрый has:
function unique<T>(items: T[]): T[] {
return [...new Set(items)];
}
WeakMap и WeakSet
const meta = new WeakMap<object, { visited: boolean }>();
function mark(o: object): void {
meta.set(o, { visited: true });
}
Разбор:
- Ключи только объекты; не итерируются целиком.
- Не удерживают объект от GC — метаданные исчезают вместе с ключом.
- Типы:
WeakMap<K extends object, V>,WeakSet<T extends object>— Справочник — составные типы.
Методы массивов и вывод типов
const nums = [1, 2, 3];
const doubled = nums.map((n) => n * 2); // number[]
const evens = nums.filter((n) => n % 2 === 0); // number[]
const sum = nums.reduce((acc, n) => acc + n, 0); // number
С пользовательским типом:
interface Product {
sku: string;
price: number;
}
const catalog: Product[] = [
{ sku: "a", price: 10 },
{ sku: "b", price: 20 },
];
const total = catalog.reduce((sum, p) => sum + p.price, 0);
const skus = catalog.map((p) => p.sku);
Разбор:
- Тип
accвreduceвыводится из начального значения (0→ number). - Явная аннотация нужна, если начальное значение шире:
reduce<number>(..., 0).
flatMap:
const lines = ["a b", "c"].flatMap((line) => line.split(" "));
// string[]
Кортеж как результат: Promise и ошибки
Результат в виде union: либо поле data, либо поле error — без отдельного класса:
type Result<T> = [error: null, data: T] | [error: Error, data: null];
async function loadSafe(): Promise<Result<string>> {
try {
const data = await fetchText();
return [null, data];
} catch (e: unknown) {
return [e instanceof Error ? e : new Error(String(e)), null];
}
}
Разбор:
- Именованные элементы кортежа документируют позиции.
- Для сложных сценариев чаще используют discriminated union — 10.md, 17.md.
Массивы в generic-коде
function first<T>(items: readonly T[]): T | undefined {
return items[0];
}
function pair<A, B>(a: A, b: B): [A, B] {
return [a, b];
}
Подробнее о параметрах типа — 24.md.
Совместимость readonly и мутабельных массивов
function sortInPlace(nums: number[]): void {
nums.sort();
}
const readonlyNums: readonly number[] = [3, 1, 2];
// sortInPlace(readonlyNums); // ошибка без копии
sortInPlace([...readonlyNums]);
Разбор:
- Мутабельный параметр не принимает
readonlyбезопасно. - Spread создаёт новый массив для изменения.
JSON и коллекции
JSON.parse возвращает any (или unknown при настройках). Для массивов:
function parseStringArray(raw: unknown): string[] {
if (!Array.isArray(raw)) throw new Error("Expected array");
if (!raw.every((x) => typeof x === "string")) {
throw new Error("Expected string[]");
}
return raw;
}
Не утверждайте as string[] без проверки — 6.md.
Частые ошибки
| Ошибка | Причина | Что делать |
|---|---|---|
never[] у const a = [] | нет контекста типа | const a: Item[] = [] |
| Кортеж vs массив | путаница длины | [A,B] только для фикс. позиций |
Record без всех ключей union | неполный объект | добавить ключи или Partial |
map.get без проверки | undefined | guard или getOrThrow |
Мутация readonly | push/sort | копия [...arr] |
as T[] на JSON | неверные данные | runtime guard |
Практика
- Опишите
Record<HttpStatus, string>для кодов 200, 404, 500 и сообщений. - Реализуйте
unique<T>(items: T[])черезSet. - Храните пользователей в
Map<string, User>и напишитеgetOrThrow. - Создайте кортеж
Rgbи функциюtoCss([r,g,b])с типомstring. - Опишите
Point2D/Point3Dс labeled tuples и убедитесь, что лишний элемент даёт ошибку компиляции. - Пройдите
catalog.reduceс явным типом аккумулятора, чтобы найти самый дорогойProduct.
Смежные статьи
- Типы — union,
readonly,satisfies - Функции — callback в
map/filter - Дженерики —
Array<T>, ограничения - Объекты и классы — поля-коллекции в классах
- Циклы —
for..ofпо итерируемым - Справочник — Справочник — составные типы
- JS: 18