`{}` vs `object` vs `Object` in TypeScript.

With this post I’ll never be confused again about their difference.

type Object

The type Object refers to the Object constructor in JavaScript.

Recall the prototype chain, (almost) every object has Object.prototype at the end of their prototype chain, so (almost) every object is instance of Object.

ts
({}).__proto__ === Object.prototype // true
([]).__proto__.__proto__) === Object.prototype); // true
new Map().__proto__.__proto__ === Object.prototype; // true
ts
({}).__proto__ === Object.prototype // true
([]).__proto__.__proto__) === Object.prototype); // true
new Map().__proto__.__proto__ === Object.prototype; // true

For primitive values(except null and undefined), when calling the methods on them, a wrapper object will be created so primitive values have the prototype chain as well

ts
const a = 1;
a.__proto__.__proto__ === Object.prototype; // true
const b = symbol();
b.__proto__.__proto__ == Object.prototype; // true
ts
const a = 1;
a.__proto__.__proto__ === Object.prototype; // true
const b = symbol();
b.__proto__.__proto__ == Object.prototype; // true

So, every non-null values could be typed as Object.

ts
let a: Object = 1;
let b: Object = {};
let c: Object = new Map();
ts
let a: Object = 1;
let b: Object = {};
let c: Object = new Map();

But Object type is not recommended officially.

❌ Don’t ever use the types Number, String, Boolean, Symbol, or Object These types refer to non-primitive boxed objects that are almost never used appropriately in JavaScript code.

type {}

{} is an empty object literal and it is (almost) the same as Object - meaning they are assignable to each other, TypeScript doesn’t complain about code below

ts
declare let a: {};
declare let b: Object;
a = b;
b = a;
ts
declare let a: {};
declare let b: Object;
a = b;
b = a;

Then what’s the difference ? The slight difference is that Object is more stricter about the prototype methods.

ts
let a:{} = {
toString() {
(method) Object.toString(): string
return 3
}
}
 
let b: Object = {
toString() {
Type '() => number' is not assignable to type '() => string'. Type 'number' is not assignable to type 'string'.2322Type '() => number' is not assignable to type '() => string'. Type 'number' is not assignable to type 'string'.
(method) Object.toString(): string
return 3
}
}
ts
let a:{} = {
toString() {
(method) Object.toString(): string
return 3
}
}
 
let b: Object = {
toString() {
Type '() => number' is not assignable to type '() => string'. Type 'number' is not assignable to type 'string'.2322Type '() => number' is not assignable to type '() => string'. Type 'number' is not assignable to type 'string'.
(method) Object.toString(): string
return 3
}
}

We can see that Object strictly checks the return type, but {} ignore the return value. I don’t know why.

Why I mention (almost) in previous section about Object is that we actually can create some object which doesn’t fall into its category.

ts
const obj = Object.create(null);
const obj: any
ts
const obj = Object.create(null);
const obj: any

This obj doesn’t have prototype, so obj.toString() throws an error while TypeScript types it as any which is not sound.

In my humble opinion, {} would be a better option to type it if TypeScript has make it explicit about such nuances, since clearly this obj is not of type Object. But as shown above, {} and Object doesn’t make much difference for now.

object

object simply represents non primitive values, meaning NOT number, string, symbol, null, boolean, undefined or bigint. (ref)

Summary

So now we can understand why unknown means {} | null | undefined, and here is the rule of thumb:

  1. for the objects we usually means, use object.
  2. for non-null values, use {}.

😳 Would you like to share my post to more people ?    

❮ Prev: 2022 Review and 2023 Plan for JSer

Next: Distributivity in Typescript