Menu

Объектные типы TypeScript: опциональные и readonly-свойства

Как типизировать объекты в TypeScript: встроенные объектные типы, опциональные свойства с ?, readonly-свойства, вложенные объекты, методы, проверка лишних свойств и разница между object, {} и Object.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Объектный тип в TypeScript перечисляет свойства объекта и тип каждого из них: { name: string; age: number }. Добавьте ?, чтобы сделать свойство опциональным, и readonly, чтобы запретить его переприсваивание. Тип можно написать прямо на месте или дать ему имя через type или interface, чтобы переиспользовать.

Как записывать объектные типы

Свойства разделяются ; или , (работают оба, обычно пишут ;), и перевода строки тоже достаточно. Встроенный тип подходит для разового параметра; всё, что используется дважды, стоит назвать.

// Inline, in a parameter
function area(rect: { width: number; height: number }): number {
    return rect.width * rect.height;
}

// Named with a type alias
type Rect = { width: number; height: number };

// Named with an interface (the same shape)
interface RectShape {
    width: number;
    height: number;
}

type и interface одинаково хорошо описывают формы объектов. Различия (слияние объявлений, объединения) разобраны на странице interface и type.

Обращение к свойству, которого нет в типе, даёт ошибку компиляции: point.z выдаёт TS2339, Property 'z' does not exist on type '{ x: number; y: number; }'.

Опциональные свойства

? после имени позволяет не указывать свойство. Чтение опционального свойства даёт T | undefined, поэтому TypeScript заставляет обработать случай отсутствия, прежде чем использовать значение.

Вызов метода у опционального свойства без проверки даёт ошибку: p.nickname.toUpperCase() падает с TS18048, 'p.nickname' is possibly 'undefined'. Используйте опциональную цепочку (p.nickname?.toUpperCase()), когда результат undefined допустим.

prop?: T и prop: T | undefined это не одно и то же. Первое позволяет ключу отсутствовать; второе требует ключ, хотя его значение может быть undefined:

Readonly-свойства

readonly запрещает переприсваивать свойство после создания объекта. Это проверка только времени компиляции, и она поверхностная: объект или массив, хранящийся в readonly-свойстве, всё равно можно менять изнутри.

Вывод показывает оба ограничения: id действительно изменился во время выполнения (о том, что он readonly, знал только компилятор), а массив внутри был изменён. Для readonly-массива используйте readonly string[]; чтобы сделать readonly сразу все свойства, используйте Readonly<Order>.

Проверка лишних свойств

Когда вы присваиваете объектный литерал напрямую типизированной переменной или передаёте его сразу в типизированный параметр, TypeScript отвергает любое свойство, которого нет в типе. Лишние свойства в свежем литерале почти всегда оказываются опечатками.

index.ts(8,8): error TS2561: Object literal may only specify known properties, but 'colour' does not exist in type 'Options'. Did you mean to write 'color'?

Код ошибки TS2561, потому что компилятор нашёл близкое совпадение; лишнее свойство без похожего имени даёт TS2353, Object literal may only specify known properties, and 'z' does not exist in type 'Point'. Без этой проверки опечатка скомпилировалась бы, color оказался бы undefined, и программа молча рисовала бы чёрным. Проверка работает только для свежих литералов. Объект, который уже лежит в переменной, может нести лишние свойства, потому что типизация в TypeScript структурная: значение подходит к типу, если у него есть как минимум обязательные свойства.

Вложенные объекты и методы

Объектные типы могут быть вложенными и описывать методы как через синтаксис методов, так и через свойство с функциональным типом.

Для глубоких или переиспользуемых форм назовите внутренний тип (type Address = { ... }) и ссылайтесь на него, или извлеките его из внешнего типа индексированным доступом, Company["address"], как в последних строках.

object, {} и Object

Три типа звучат похоже, но означают разное:

ТипПринимаетОтвергает
objectлюбое непримитивное значение: {}, [], функции, экземпляры классов5, "a", true, null, undefined
{}любое значение, кроме null и undefined, включая примитивыnull, undefined
Objectто же, что {}, плюс проверка, что встроенные члены вроде toString сохраняют совместимые типыnull, undefined
{ x: number }любое значение с числовым xзначения без x

{} не означает «пустой объект»; он означает «не null и не undefined». Чтобы принимать любой объект с неизвестными ключами, используйте Record<string, unknown>; для отображения ключей на значения используйте индексную сигнатуру или Record, как показано на странице словарь. Чаще всего конкретная форма лучше любого из трёх.

Часто задаваемые вопросы

Как описать тип объекта в TypeScript?

Перечислите свойства и их типы в фигурных скобках: { name: string; age: number }. Такой тип можно написать прямо в аннотации или дать ему имя через type User = { ... } или interface User { ... } и переиспользовать. Свойства разделяются ; или ,.

Как сделать свойство опциональным в TypeScript?

Поставьте ? после имени свойства: { name: string; nickname?: string }. Объект может не содержать nickname, а чтение этого свойства даёт string | undefined, поэтому перед использованием как строки его нужно проверить или задать значение по умолчанию (user.nickname ?? user.name).

Чем prop?: string отличается от prop: string | undefined?

С prop?: string ключ можно не указывать вовсе. С prop: string | undefined ключ обязателен, хотя его значение может быть undefined, поэтому {} даёт ошибку компиляции. Чтение в обоих случаях даёт string | undefined.

Чем отличаются object, {} и Object в TypeScript?

object означает любое непримитивное значение (объекты, массивы, функции) и не принимает 5 или "a". {} означает любое значение, кроме null и undefined, включая примитивы. Object почти то же самое, что {}, но ещё проверяет, что встроенные методы вроде toString сохраняют совместимые типы. Используйте object, а лучше конкретную форму, а не {} или Object.

Почему TypeScript пишет, что объектный литерал может указывать только известные свойства?

Это проверка лишних свойств: ошибка TS2353 или TS2561, когда компилятор может подсказать свойство, которое вы, скорее всего, имели в виду. Когда свежий объектный литерал присваивается напрямую типизированной переменной или параметру, любое свойство, которого нет в типе, отмечается, потому что обычно это опечатка. Присвоение объекта, хранящегося в другой переменной, проверку пропускает: структурная типизация допускает лишние свойства.

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ