Типизация объектов
Типизация объектов в TypeScript — описание формы объекта: обязательных и опциональных свойств, их типов, модификаторов (
readonly), index signatures — через inline-тип,type aliasилиinterface.
Зачем нужно
Объекты — основная структура данных в JavaScript. TypeScript позволяет точно описать ожидаемую форму объекта, исключая обращение к несуществующим полям, ошибки при передаче объекта с неверной структурой и опечатки в именах свойств.
Где используется
- Модели данных:
User,Product,Order - Параметры функций и возвращаемые значения
- Конфигурационные объекты
- Словари и маппинги
Основной контент
Inline-типизация объекта
// Прямо в аннотации переменной или параметра
const user: { id: string; name: string; email: string } = {
id: "u-1",
name: "Alice",
email: "alice@example.com",
};
function greet(person: { name: string; age?: number }): string {
const age = person.age !== undefined ? `, ${person.age}` : "";
return `Hello, ${person.name}${age}!`;
}
Опциональные (?) и readonly свойства
interface Config {
host: string; // обязательное
port?: number; // опциональное: number | undefined
readonly debug: boolean; // только чтение
readonly id: string;
}
const config: Config = { host: "localhost", debug: false, id: "c-1" };
// config.debug = true; // Error — readonly
// config.port; // number | undefined — нужна проверка
Вложенные объекты
interface Address {
street: string;
city: string;
country: string;
zip?: string;
}
interface User {
id: string;
name: string;
address: Address;
contacts?: {
phone?: string;
telegram?: string;
};
}
const alice: User = {
id: "u-1",
name: "Alice",
address: {
street: "Ленина 1",
city: "Москва",
country: "Россия",
},
};
console.log(alice.address.city); // string
console.log(alice.contacts?.phone); // string | undefined
Index Signatures — динамические ключи
// Объект с произвольными string-ключами
interface StringMap {
[key: string]: string;
}
const translations: StringMap = {
hello: "привет",
goodbye: "пока",
};
// Смешанный: конкретные + динамические
interface FlexibleRecord {
id: string; // конкретный ключ
[extra: string]: string; // динамические — ДОЛЖНЫ быть совместимы с id
}
Record для словарей
// Вместо index signature часто удобнее Record
type UserMap = Record<string, User>; // string → User
type ScoreMap = Record<string, number>; // string → number
type Role = "admin" | "user" | "guest";
type PermissionMap = Record<Role, string>; // конечный набор ключей
const perms: PermissionMap = {
admin: ["read", "write", "delete"],
user: ["read", "write"],
guest: ["read"],
};
Destructuring с типами
// TypeScript выводит типы при деструктурировании
const { id, name, address: { city } } = alice;
// id: string, name: string, city: string
// Деструктурирование с ренеймингом
const { id: userId, name: userName } = alice;
// userId: string, userName: string
// С дефолтными значениями
function greet({ name, age = 0 }: { name: string; age?: number }) {
return `${name}, ${age}`;
}
Excess property check
interface Point { x: number; y: number }
// Избыточные свойства при прямом присваивании — ошибка
// const p: Point = { x: 0, y: 0, z: 0 }; // Error — z лишнее
// Но через промежуточную переменную — OK (duck typing)
const point3D = { x: 0, y: 0, z: 0 };
const p: Point = point3D; // OK — { x, y, z } структурно совместим с { x, y }
Частые ошибки
- Обращаться к опциональному полю без проверки —
config.port.toFixed()— ошибка еслиportне указан. - Смешивать index signature с несовместимыми конкретными полями —
{ id: string; [k: string]: number }— ошибка,id: stringне совместим сnumber. - Ожидать строгую типизацию при промежуточной переменной — excess property check работает только при прямом присваивании.
- Мутировать readonly поля — TypeScript запретит на этапе компиляции, но
Object.freezeнужен для рантайм-защиты.
Связанные темы
- interface -- объявление и использование
- type alias -- объявление
- Partial, Required, Readonly
- Pick, Omit, Record
- Readonly свойства
- _MOC TypeScript