Чотири типи, які легко сплутати, і вони приймають різні множини значень.
| Тип | Що приймає |
|---|---|
unknown |
будь-що, включно з null і undefined |
{} |
будь-що, крім null і undefined (і рядки, і числа!) |
object |
лише не-примітиви: об'єкти, масиви, функції |
Object |
майже як {} (будь-що з методами Object.prototype), застарілий запис |
const a: {} = 5; // ок - число не null і не undefined
const b: {} = 'текст'; // ок
const c: {} = null; // помилка
const d: object = { }; // ок
const e: object = []; // ок
const f: object = 5; // помилка - примітив
Пастка {}. Його часто пишуть, маючи на увазі «порожній об'єкт» чи «якийсь об'єкт», а отримують «будь-яке ненульове значення». Правило лінтера @typescript-eslint/no-empty-object-type попереджає саме про це.
Що використовувати насправді:
- «будь-яке значення, перевірю пізніше» -
unknown; - «будь-який об'єкт, ключі невідомі» -
Record<string, unknown>. Доступ до поля даєunknown, і код змушений перевіряти; - «не-примітив» (наприклад, для
WeakMap-ключів чи функцій, що працюють з посиланнями) -object; - «справді порожній об'єкт, без полів» -
Record<PropertyKey, never>; - «будь-що, крім null/undefined» у узагальненнях - обмеження
T extends {}, наприклад у власномуNonNullable.
{} в узагальненнях - корисний інструмент: NonNullable<T> визначено як T & {} - перетин прибирає null і undefined з об'єднання.
Object з великої літери - тип обгортки, що лишився з ранніх версій. Документація радить ніколи його не використовувати.
Чому object не дає доступу до полів:
function print(value: object) {
value.name; // помилка: у object немає властивості name
}
object лише гарантує, що це не примітив. Щоб читати поля, тип має їх описувати або бути Record<string, unknown> з подальшою перевіркою.