Don't use `extends object`

May 8, 2024 Updated: May 9, 2024

Don't use `extends object`

Table of Contents

What is the object type?

Marius Schulz gives a great explanation of the object type in his article here. Essentially, the object type represents any non-primitive type.

ts
// All primitive types
type Primitive = string | boolean | number | bigint | symbol | null | undefined;

// All non-primitive types
type NonPrimitive = object;

This is less than ideal if you want to avoid extending built-in classes like Date or Blob. It also extends branded primitives such as string & Brand<"brand">, because brand types are objects. The object type is a bit of a catch-all, and it’s not always clear what you’re extending.

In other words, you should only extend the object type if you’re looking to match any non-primitive type.

Dictionaries

A dictionary (or record) is a type of object that is made up of key-value pairs. If your objective is to only extend dictionaries, you can instead extend the Record<PropertyKey, unknown> type.

ts
type IsDictionary<T extends object> = T extends Record<PropertyKey, unknown> ? true : false;

type CheckDate = IsDictionary<Date>;
//   ^? type CheckDate = false
type CheckObject = IsDictionary<Object>;
//   ^? type CheckObject = false
type CheckDictionary = IsDictionary<{ a: number, b: string }>;
//   ^? type CheckDictionary = true

The Difference Between {}, object, and Record<PropertyKey, unknown>

This table shows the types of values that are assignable to {}, object, and Record<PropertyKey, unknown>.

  • You can assign anything to {}, except for null and undefined.
  • You can assign anything that’s not a primitive to object. Primitives are strings, booleans, numbers, big integers, symbols, null and undefined.
  • You can only assign dictionaries to Record<PropertyKey, unknown>.
Value is assignable to {} object Record<PropertyKey, unknown>
null No No No
undefined No No No
"string" Yes No No
true Yes No No
42 Yes No No
42n Yes No No
Symbol() Yes No No
Date Yes Yes No
() => {} Yes Yes No
[1, 2] Yes Yes No
[] Yes Yes No
{foo: "bar"} Yes Yes Yes
{} Yes Yes Yes