Headshot of Marius Schulz
Marius Schulz Front End Engineer

TypeScript 2.2: The object Type

TypeScript 2.2 introduced a new type called object. It represents any non-primitive type. The following types are considered to be primitive types in JavaScript:

  • boolean
  • number
  • string
  • symbol
  • null
  • undefined

All other types are considered to be non-primitive types. The new object type represents exactly these:

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

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

Let's see how object lets us write more accurate type declarations.

Type Declarations Using the object Type

With the release of TypeScript 2.2, the type declarations for the standard library have been updated to make use of the new object type. For instance, the Object.create() and Object.setPrototypeOf() methods now specify the type object | null for their prototype parameters:

interface ObjectConstructor {
   * Creates an object that has the specified prototype or that has null prototype.
   * @param o Object to use as a prototype. May be null.
  create(o: object | null): any;

   * Sets the prototype of a specified object o to  object proto or null. Returns the object o.
   * @param o The object to change its prototype.
   * @param proto The value of the new prototype or null.
  setPrototypeOf(o: any, proto: object | null): any;

  // ...

Passing a primitive value as a prototype to either Object.setPrototypeOf() or Object.create() results in a TypeError being thrown at run-time. TypeScript now catches such mistakes and issues an error at compile-time:

const proto = {};

Object.create(proto);     // OK
Object.create(null);      // OK
Object.create(undefined); // Error
Object.create(1337);      // Error
Object.create(true);      // Error
Object.create("oops");    // Error

Another use case for the object type is the WeakMap data structure that was introduced as part of ES2015. Its keys must be objects and cannot be primitive values. This requirement is now reflected in the type definition:

interface WeakMap<K extends object, V> {
  delete(key: K): boolean;
  get(key: K): V | undefined;
  has(key: K): boolean;
  set(key: K, value: V): this;

object vs. Object vs. {}

Perhaps confusingly, TypeScript defines several types that have a similar name but represent different concepts:

  • object
  • Object
  • {}

We've already looked at the new object type above. Let's now discuss what Object and {} represent.

The Object Type

TypeScript defines another type with almost the same name as the new object type, and that's the Object type. While object (lowercased) represents all non-primitive types, Object (uppercased) describes functionality that is common to all JavaScript objects. That includes the toString() and the hasOwnProperty() methods, for example.

Within the lib.es6.d.ts file shipping with TypeScript, the Object type is defined as follows:

interface Object {
  // ...

  /** Returns a string representation of an object. */
  toString(): string;

  /** Returns a date converted to a string using the current locale. */
  toLocaleString(): string;

  /** Returns the primitive value of the specified object. */
  valueOf(): Object;

   * Determines whether an object has a property with the specified name.
   * @param v A property name.
  hasOwnProperty(v: string): boolean;

   * Determines whether an object exists in another object's prototype chain.
   * @param v Another object whose prototype chain is to be checked.
  isPrototypeOf(v: Object): boolean;

   * Determines whether a specified property is enumerable.
   * @param v A property name.
  propertyIsEnumerable(v: string): boolean;

The Empty Object Type {}

There's yet another type which is quite similar: {}, the empty object type. It describes an object that has no members on its own. TypeScript issues a compile-time error when you try to access arbitrary properties on such an object:

// Type {}
const obj = {};

// Error: Property 'prop' does not exist on type '{}'.
obj.prop = "value";

However, you can still use all properties and methods defined on the Object type, which are implicitly available via JavaScript's prototype chain:

// Type {}
const obj = {};

// "[object Object]"

This post is part of the TypeScript Evolution series:

  1. TypeScript 2.0: Non-Nullable Types
  2. TypeScript 2.0: Control Flow Based Type Analysis
  3. TypeScript 2.0: Acquiring Type Declaration Files
  4. TypeScript 2.0: Read-Only Properties
  5. TypeScript 2.0: Tagged Union Types
  6. TypeScript 2.0: More Literal Types
  7. TypeScript 2.0: The never Type
  8. TypeScript 2.0: Built-In Type Declarations
  9. TypeScript 2.1: async/await for ES3/ES5
  10. TypeScript 2.1: External Helpers Library
  11. TypeScript 2.1: Object Rest and Spread
  12. TypeScript 2.1: keyof and Lookup Types
  13. TypeScript 2.1: Mapped Types
  14. TypeScript 2.1: Improved Inference for Literal Types
  15. TypeScript 2.1: Literal Type Widening
  16. TypeScript 2.1: Untyped Imports
  17. TypeScript 2.2: The object Type
  18. TypeScript 2.2: Dotted Properties and String Index Signatures
  19. TypeScript 2.2: Null-Checking for Expression Operands
  20. TypeScript 2.2: Mixin Classes
  21. TypeScript 2.3: Generic Parameter Defaults
  22. TypeScript 2.3: The --strict Compiler Option
  23. TypeScript 2.3: Type-Checking JavaScript Files with --checkJs
  24. TypeScript 2.3: Downlevel Iteration for ES3/ES5
  25. TypeScript 2.4: String Enums
  26. TypeScript 2.4: Weak Type Detection
  27. TypeScript 2.4: Spelling Correction
  28. TypeScript 2.4: Dynamic import() Expressions
  29. TypeScript 2.5: Optional catch Binding
  30. TypeScript 2.6: JSX Fragment Syntax
  31. TypeScript 2.7: Numeric Separators
  32. TypeScript 2.7: Strict Property Initialization
  33. TypeScript 2.8: Per-File JSX Factories
  34. TypeScript 2.8: Conditional Types
  35. TypeScript 2.8: Mapped Type Modifiers