Объекты: type и interface
Форма объектов, необязательные и readonly-поля, Record, type или interface.
Обновлено
Что значит типизировать объект
Вы описываете форму объекта: какие у него ключи и какого типа каждое значение. Затем TS проверяет, что каждый используемый объект соответствует этой форме.
type Product = {
id: number
name: string
price: number
tags?: string[] // необязательное — может отсутствовать
readonly sku: string // нельзя изменить после создания
}
const p: Product = { id: 1, name: 'Laptop', price: 4000, sku: 'LP-1' }
p.sku = 'X' // ✗ readonly
p.color // ✗ нет в Producttype или interface — различия и когда
Оба описывают форму объекта. В 90% случаев они взаимозаменяемы.
type |
interface |
|
|---|---|---|
| Объекты | ✓ | ✓ |
Union, кортежи, примитивы, функции ('a' | 'b') |
✓ | ✗ |
| Расширение | type B = A & { x: number } |
interface B extends A { x: number } |
| Declaration merging (два объявления с одним именем объединяются) | ✗ | ✓ |
| Сообщения об ошибках | иногда длиннее | иногда понятнее |
Практическая рекомендация: type по умолчанию (подходит для всего), interface — когда пишете библиотеку, которую будут расширять, или когда так принято в команде. Главное — единообразие.
Необязательные и readonly-свойства
type User = {
id: number
email: string
nickname?: string // string | undefined
readonly createdAt: Date
}
function displayName(user: User) {
return user.nickname ?? user.email // TS заставляет обработать отсутствие
}Словари: index signature и Record
Когда ключи заранее неизвестны:
type Prices = { [productId: string]: number }
type Prices2 = Record<string, number> // то же самое, читается лучше
const stock: Record<'ru' | 'kz', number> = { ru: 10, kz: 2 } // фиксированные ключи из unionВложенные и составные типы
type Address = { city: string; zip: string }
type Customer = { name: string; address: Address }
type Timestamps = { createdAt: Date; updatedAt: Date }
type Post = { title: string } & Timestamps // пересечение: есть всё из обоихСтруктурная типизация
TS сравнивает объекты по форме, а не по имени: принимается любой объект с нужными полями.
type Point = { x: number; y: number }
const p3 = { x: 1, y: 2, z: 3 }
const p: Point = p3 // ✓ есть x и y — лишнее поле не мешает(Исключение: литерал объекта, написанный прямо с лишними полями, даёт ошибку — «excess property check», — чтобы ловить опечатки.)
Коротко
- Типизируете форму объекта;
?— необязательное,readonly— неизменяемое. typeпо умолчанию;interface— для расширения / библиотек.Record<K, V>для словарей;&для объединения.