Avatar ·

Как объявить массив с элементами разных типов в TypeScript?

Всем привет! Я только начал изучать TypeScript и столкнулся с проблемой. В обычном JavaScript я могу легко создать массив, содержащий элементы разных типов, например: ```javascript const data = [1, "hello", true, { name: "John" }]; ``` Но когда я пытаюсь сделать то же самое в TypeScript, компилятор ругается или выводит тип, который мне не подходит. Я попробовал явно указать тип массива: ```typescript const data: Array = [1, "hello", true]; ``` Но это не работает — TypeScript говорит, что Array требует указания типа элементов. Тогда я попробовал `Array`: ```typescript const data: Array = [1, "hello", true]; ``` Это вроде работает, но я слышал, что использовать `any` — плохая практика, потому что теряется типобезопасность. Мне нужно объявить массив, в котором могут быть только строки, числа и булевы значения (или какие-то конкретные типы). Как правильно это сделать? Есть ли способ указать, что массив может содержать элементы разных заранее известных типов, но не любые? И как потом работать с такими элементами, чтобы TypeScript понимал, какой тип у каждого элемента в конкретный момент? Заранее спасибо за помощь! Пример моего кода: ```typescript // Что я хочу: массив, который может содержать только строки и числа const mixedArray = [42, "answer", 7, "lucky"]; // Как правильно объявить тип для такого массива? // И как потом безопасно обращаться к элементам? ``` <<>>

Использование 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 доступно
  }
}

Этот подход даёт максимальную типобезопасность при работе с разнородными данными в массивах.