Как объявить массив с элементами разных типов в TypeScript?
Использование Union Types для массивов в TypeScript
Проблема, с которой вы столкнулись, очень распространена среди новичков в TypeScript. Ответ кроется в использовании union types (объединений типов). Вместо того чтобы использовать any (который действительно убивает всю пользу от TypeScript), вы можете точно указать, какие типы разрешены в массиве.
Вот как это делается:
// Массив, который может содержать только строки и числа
const mixedArray: (string | number)[] = [42, "answer", 7, "lucky"];
// Альтернативный синтаксис с Array<>
const mixedArrayAlt: Array<string | number> = [42, "answer", 7, "lucky"];
Как это работает
Синтаксис (string | number)[] означает: "массив, каждый элемент которого является либо строкой, либо числом". TypeScript будет следить, чтобы вы не добавили туда, например, булево значение или объект. Попробуйте добавить true — компилятор выдаст ошибку.
Работа с элементами такого массива
Когда вы достаёте элемент из такого массива, TypeScript знает, что это может быть string | number. Чтобы безопасно работать с элементом, нужно использовать type guards (проверки типа):
function processElement(element: string | number): void {
if (typeof element === 'string') {
// TypeScript знает, что здесь element — это строка
console.log(element.toUpperCase());
} else {
// TypeScript знает, что здесь element — это число
console.log(element.toFixed(2));
}
}
for (const item of mixedArray) {
processElement(item);
}
Более сложные случаи
Если вам нужно больше типов, просто перечислите их в union:
// Массив со строками, числами и булевыми значениями
const complexArray: (string | number | boolean)[] = [42, "hello", true, 100, "world"];
Tuple types (кортежи) — когда важен порядок
Если вы точно знаете, что массив всегда имеет определённую длину и типы элементов на конкретных позициях, используйте tuple (кортеж):
// Кортеж: первый элемент — число, второй — строка
const pair: [number, string] = [42, "answer"];
// Кортеж с тремя элементами разных типов
const triple: [string, number, boolean] = ["hello", 42, true];
// Доступ к элементам по индексу даёт правильный тип
const num = pair[0]; // TypeScript знает, что это number
const str = pair[1]; // TypeScript знает, что это string
Почему это лучше, чем any?
- Типобезопасность: Вы не сможете случайно добавить элемент неправильного типа.
- Автодополнение: IDE будет предлагать правильные методы для каждого типа после проверки.
- Читаемость: Код сам документирует, какие данные ожидаются.
- Рефакторинг: Если вы измените типы, TypeScript укажет все места, которые нужно исправить.
Пример из реального проекта
Часто это используется для работы с API, где приходит массив данных разных типов:
type ApiResponse = string | number | { id: number; name: string };
function processApiData(data: ApiResponse[]): void {
for (const item of data) {
if (typeof item === 'string') {
// Обработка строки
} else if (typeof item === 'number') {
// Обработка числа
} else {
// Обработка объекта
console.log(item.name);
}
}
}
Используйте union types для массивов — это правильный и безопасный путь в TypeScript.
<<Альтернативный подход: Type Aliases и пользовательские типы
Если union type становится слишком длинным или вы используете его в нескольких местах, лучше вынести его в отдельный тип (type alias):
// Определяем тип для элемента массива
type MixedElement = string | number | boolean;
// Используем его для объявления массива
const data: MixedElement[] = [42, "hello", true];
// Или как Array<MixedElement>
const data2: Array<MixedElement> = [42, "hello", true];
Когда это особенно полезно
- Когда типов больше 3-4 — код становится чище
- Когда один и тот же тип массива используется в разных функциях
- Для документирования — имя типа говорит о назначении данных
Использование readonly для иммутабельности
Если массив не должен изменяться, добавьте readonly:
const fixedData: readonly (string | number)[] = [42, "answer"];
// fixedData.push(100); // Ошибка! Мутация запрещена
Дискриминированные объединения для сложных случаев
Если элементы массива — объекты с разной структурой, используйте дискриминированные объединения:
type Success = { status: 'success'; data: string };
type Error = { status: 'error'; message: string };
type Result = Success | Error;
const results: Result[] = [
{ status: 'success', data: 'OK' },
{ status: 'error', message: 'Failed' }
];
// TypeScript сужает тип при проверке
for (const r of results) {
if (r.status === 'success') {
console.log(r.data); // ✅ data доступно
} else {
console.log(r.message); // ✅ message доступно
}
}
Этот подход даёт максимальную типобезопасность при работе с разнородными данными в массивах.
Ответов пока нет.
Войдите чтобы оставить ответ