Для описания сложных структур данных в TypeScript используют интерфейсы (interface) и псевдонимы типов (type). Можно комбинировать их с объектами, массивами и вложенными типами для строгой типизации сложных коллекций.
Для вложенного объекта:
interface Address { city: string; zip: number; } interface UserProfile { name: string; age: number; address: Address; }
Для массива с разными типами:
// Кортеж let tuple: [string, number] = ['John', 30]; // Массивы с union-типами let arr: (string | number)[] = [1, 'a', 2, 'b']; // Массив объектов let users: UserProfile[] = [ {...}, {...} ];
Когда структуры сложны и есть опциональные поля, используют ?, комбинируют с типами Partial, Record, Mapped Types, или рекурсивные типы для вложенных деревьев.
Вопрос: Можно ли использовать интерфейс для описания массива c разнотипными элементами (например, [string, number, boolean])?
Ответ: Нет. Для такого случая лучше использовать кортежи — интерфейсы не подходят для фиксированных позиций и типов. Кортежи позволяют строго задать типы по каждой позиции.
type MyTuple = [string, number, boolean]; let foo: MyTuple = ['ok', 12, false];
История
На проекте неправильно описали сложную структуру: для массива смешанных типов использовали any[] вместо корректного кортежа или union-типа. В результате, в одном из элементов оказалось значение не того типа, что привело к ошибке в бизнес-логике (арифметическая операция со строкой).
История
В структуре данных глубоко вложенные объекты были объявлены без использования рекурсивных типов или Partial. Попытка добавить к узлу дерева новое поддерево вызывала ошибку компилятора, и разработчики обходили это через downcast в any, что затем вызвало runtime-баги в продакшне.
История
Объект с описанием пользовательского профиля был частично опциональным, но разработчик не использовал ?. При получении данных с сервера TypeScript не ругался, а приложение падало, пытаясь обратиться к полям, которых не существовало в объекте.