Как типизировать массивы, кортежи и коллекции в TypeScript?
Вопрос
Как типизировать массивы (T[] и Array<T>), кортежи (tuples) и коллекции вроде Map, Set, Record?
Короткий ответ
T[] и Array<T> — это два равнозначных синтаксиса для типизации массива с элементами одного типа. Кортеж ([T, U, ...]) описывает массив фиксированной длины, где тип каждого элемента задан по позиции. Map<K, V>, Set<T> и Record<K, V> — дженерик-типы для типизации ключ-значение и множеств.
Подробный ответ
T[] vs Array<T> — семантически идентичны, различие чисто синтаксическое. T[] короче и привычнее, Array<T> удобен для сложных типов (избегает лишних скобок) и явно показывает, что это дженерик:
let names: string[] = ['a', 'b']
let ids: Array<number> = [1, 2, 3]
let matrix: number[][] = [[1, 2], [3, 4]] // массив массивов
Кортежи (tuples) — фиксированной длины массивы, где важен и тип, и позиция элемента. В отличие от обычного массива, где все элементы одного типа и длина не ограничена:
let point: [number, number] = [10, 20]
let entry: [string, number, boolean] = ['age', 30, true]
// именованные элементы (только для документации, не влияют на проверку типов)
let range: [start: number, end: number] = [0, 100]
// опциональные и rest-элементы
let cssRule: [property: string, value: string, important?: boolean] = ['color', 'red']
let path: [root: string, ...segments: string[]] = ['/', 'users', '42']
Кортежи часто используются как тип возврата функций, возвращающих несколько значений (например, React useState возвращает [value, setValue]).
Map<K, V> и Set<T> — типизированные версии встроенных коллекций:
const cache: Map<string, number> = new Map()
cache.set('a', 1)
const uniqueIds: Set<number> = new Set([1, 2, 3])
Record<K, V> — utility-тип для объекта, где все ключи одного типа K сопоставлены значениям типа V. По сути — типобезопасная альтернатива { [key: string]: V }, особенно полезная, когда K — union конкретных строковых литералов, потому что тогда TypeScript требует наличия всех ключей:
type Role = 'admin' | 'user' | 'guest'
const permissions: Record<Role, string[]> = {
admin: ['read', 'write', 'delete'],
user: ['read', 'write'],
guest: ['read'],
} // если забыть один из ключей Role — ошибка компиляции
Пример
type Status = 'pending' | 'success' | 'error'
// кортеж как тип возврата, аналог React useState
function useToggle(initial: boolean): [boolean, () => void] {
let state = initial
const toggle = () => { state = !state }
return [state, toggle]
}
const [isOpen, toggleOpen] = useToggle(false)
// Record для строгой карты обработчиков на все статусы
const statusHandlers: Record<Status, (msg: string) => void> = {
pending: (msg) => console.log('⏳', msg),
success: (msg) => console.log('✅', msg),
error: (msg) => console.error('❌', msg),
}
// Map для кэша с типизированными ключами и значениями
const requestCache = new Map<string, Promise<unknown>>()
function getCached<T>(key: string, loader: () => Promise<T>): Promise<T> {
if (!requestCache.has(key)) requestCache.set(key, loader())
return requestCache.get(key) as Promise<T>
}
Дополнительные вопросы
- Чем
readonly T[]отличается от обычногоT[]и когда его стоит использовать? - Как TypeScript выводит тип кортежа против обычного массива при
const arr = [1, 'a']? - В чём разница между
Record<string, T>и индексной сигнатурой{ [key: string]: T }?