>>
Правильный способ: используйте интерфейс или type для описания объекта
Ключевая идея TypeScript — описывать форму данных, а не просто указывать "это объект". Для массива объектов нужно сначала определить тип самого объекта, а затем указать, что массив состоит из таких объектов.
Шаг 1: Определите тип объекта
// Через интерфейс (рекомендуется для объектов, которые могут расширяться)
interface Task {
title: string;
done: boolean;
createdAt: Date;
}
// Или через type (если нужен union/intersection, или просто привыкли)
type Task = {
title: string;
done: boolean;
createdAt: Date;
};
Шаг 2: Объявите массив этого типа
// Вариант 1 — синтаксис с квадратными скобками (наиболее популярный)
const tasks: Task[] = [
{ title: 'Купить молоко', done: false, createdAt: new Date() },
{ title: 'Сделать зарядку', done: true, createdAt: new Date() }
];
// Вариант 2 — через generic Array<T> (менее распространён, но тоже корректен)
const tasks: Array<Task> = [
{ title: 'Купить молоко', done: false, createdAt: new Date() },
// ...
];
Почему это работает?
- TypeScript проверяет каждое свойство — если вы попытаетесь добавить объект без поля
title, компилятор выдаст ошибку.
- Автодополнение в IDE — при наборе
tasks[0]. редактор предложит title, done, createdAt.
- Безопасность типов — вы не сможете случайно присвоить массиву строку или число.
Дополнительные возможности
// Необязательные поля (?)
interface Task {
title: string;
done: boolean;
createdAt: Date;
completedAt?: Date; // может отсутствовать
}
// Только для чтения (readonly)
interface Task {
readonly id: number;
title: string;
done: boolean;
}
// Массив только для чтения
const tasks: ReadonlyArray<Task> = [...];
// или const tasks: readonly Task[] = [...];
Практический пример с функциями
interface Task {
id: number;
title: string;
done: boolean;
}
// Функция принимает массив задач
function getPendingTasks(tasks: Task[]): Task[] {
return tasks.filter(task => !task.done);
}
// Функция возвращает массив задач
function createSampleTasks(): Task[] {
return [
{ id: 1, title: 'Купить молоко', done: false },
{ id: 2, title: 'Сделать зарядку', done: true }
];
}
// Использование
const allTasks = createSampleTasks();
const pendingTasks = getPendingTasks(allTasks);
console.log(pendingTasks[0].title); // ✅ TypeScript знает, что это string
Что НЕ стоит делать
const tasks: object[] = [...] — теряете информацию о структуре, нельзя обращаться к полям.
const tasks: any[] = [...] — отключаете всю проверку типов, смысл TypeScript теряется.
const tasks = [...] as any — то же самое, только хуже (подавляет проверку даже на уровне присваивания).
Итог
Всегда определяйте интерфейс или тип для объекта, а затем используйте ИмяТипа[] или Array<ИмяТипа>. Это даёт максимальную безопасность и удобство разработки.
<<>>
Альтернативный подход: встраивание типа прямо в объявление массива
Если объект используется только один раз и не имеет смысла выносить его в отдельный интерфейс, можно объявить тип "на месте" с помощью литерала типа:
const tasks: { title: string; done: boolean; createdAt: Date }[] = [
{ title: 'Купить молоко', done: false, createdAt: new Date() },
{ title: 'Сделать зарядку', done: true, createdAt: new Date() }
];
Это работает, но имеет недостатки:
- Дублирование кода — если такой же массив понадобится в другом месте, придётся повторять тип.
- Сложность чтения — длинная строка с типом внутри квадратных скобок ухудшает читаемость.
- Ошибки при изменении — если нужно добавить поле, придётся менять тип в каждом месте использования.
Когда это может быть оправдано?
- Временные данные в небольшом скрипте или прототипе.
- Массив с уникальной структурой, которая точно не повторится (например, результат парсинга конкретного API).
- Быстрое тестирование идеи без создания лишних файлов.
Совет
Даже если вы пишете inline-тип, старайтесь использовать type или interface для сложных объектов. Это делает код самодокументируемым и упрощает рефакторинг. В больших проектах inline-типы обычно считаются плохой практикой.