TypeScript 2.2 introduces a new type called the object type, which is used to represent non-primitive types. The following types are considered primitive in JavaScript: String, Boolean, number, Bigint, symbol, NULL, and undefined.

All other types are considered non-basic. The new object type is represented as follows:

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

// All non-primitive types
type NonPrimitive = object;
Copy the code

Let’s look at the object type and see how it allows us to write more precise type declarations.

Type declaration using the object type

With TypeScript 2.2, the standard library type declarations have been updated to use the new object types. Object, for example, the create () and Object setPrototypeOf () method, now need to account for their prototype parameter specifies the Object | null type:

// node_modules/typescript/lib/lib.es5.d.ts
interface ObjectConstructor {
  create(o: object | null) :any;
  setPrototypeOf(o: any, proto: object | null) :any;
  // ...
}
Copy the code

Passing a primitive type as a prototype to Object.setProtoTypeof () or Object.create() causes a type error to be thrown at runtime. TypeScript now catches these errors and prompts them 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
Copy the code

Another use case for the Object type is the WeakMap data structure introduced as part of ES2015. Its keys must be objects, not 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;
}
Copy the code

Object vs Object vs {}

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

  • object
  • Object
  • {}

We have seen the new object type above. Now let’s discuss what Object and {} represent.

2.1 the Object type

TypeScript defines another type with almost the same name as the new object type, the Object type. This type is the type of all instances of the Object class. It is defined by the following two interfaces:

  • The Object interface defines properties on the Object. Prototype Object.
  • The ObjectConstructor interface defines the properties of the Object class.

Let’s take a look at the definitions of these two interfaces:

1. Object interface definition

// node_modules/typescript/lib/lib.es5.d.ts

interface Object {
  constructor: Function;
  toString(): string;
  toLocaleString(): string;
  valueOf(): Object;
  hasOwnProperty(v: PropertyKey): boolean;
  isPrototypeOf(v: Object): boolean;
  propertyIsEnumerable(v: PropertyKey): boolean;
}
Copy the code

ObjectConstructor interface definition

// node_modules/typescript/lib/lib.es5.d.ts

interface ObjectConstructor {
  /** Invocation via `new` */
  new(value? :any) :Object;
  /** Invocation via function calls */(value? :any) :any;

  readonly prototype: Object;

  getPrototypeOf(o: any) :any;

  / /...
}

declare var Object: ObjectConstructor;
Copy the code

All instances of the Object class inherit all properties of the Object interface. We can see that if we create a function that returns its argument:

Passing in an instance of an Object always satisfies the function’s return type — that is, requiring the return value to contain a toString() method.

// Object: Provides functionality common to all JavaScript objects.
function f(x: Object) :{ toString(): string } {
  return x; // OK
}
Copy the code

The object type is used to represent non-primitive types (undefined, NULL, Boolean, number, Bigint, string, symbol). With this type, we cannot access any properties of the value.

2.2 the Object vs Object

Interestingly, the type Object includes primitive values:

function func1(x: Object) { }
func1('semlinker'); // OK
Copy the code

Why is that? The properties of Object.prototype can also be accessed from the original values:

> 'semlinker'.hasOwnProperty === Object.prototype.hasOwnProperty
true
Copy the code

For those who are interested, check out “JavaScript boxing and unboxing.”

In contrast, the object type does not include primitive values:

function func2(x: object) {}// Argument of type '"semlinker"' 
// is not assignable to parameter of type 'object'.(2345)
func2('semlinker'); // Error
Copy the code

Note that when assigning a value to a variable of type Object, the TypeScript compiler prompts an error if the value Object attribute name conflicts with an attribute in the Object interface:

// Type '() => number' is not assignable to type 
// '() => string'.
// Type 'number' is not assignable to type 'string'.
const obj1: Object = { 
   toString() { return 123 } // Error
}; 
Copy the code

For object types, the TypeScript compiler does not display any errors:

const obj2: object = { 
  toString() { return 123}};Copy the code

Special care should also be taken when dealing with assignment operations of type Object and string index object types. Such as:

let strictTypeHeaders: { [key: string] :string } = {};
let header: object = {};
header = strictTypeHeaders; // OK
// Type 'object' is not assignable to type '{ [key: string]: string; } '.
strictTypeHeaders = header; // Error
Copy the code

In the example above, the last line produces a compilation error because {[key: string]: string} is more accurate than object. The header = strictTypeHeaders; This line does not indicate any errors because both types are non-basic and object is more generic than {[key: string]: string}.

2.3 Empty Type {}

There is another type that is very similar, the null type: {}. It describes an object that has no members. TypeScript generates a compile-time error when you try to access arbitrary properties of such an object:

// Type {}
const obj = {};

// Error: Property 'prop' does not exist on type '{}'.
obj.prop = "semlinker";
Copy the code

However, you can still use all the properties and methods defined on the Object type, which can be used implicitly through JavaScript’s prototype chain:

// Type {}
const obj = {};

// "[object Object]"
obj.toString();
Copy the code

Creating an object representing a two-dimensional coordinate point in JavaScript is simple:

const pt = {}; 
pt.x = 3; 
pt.y = 4;
Copy the code

In TypeScript, however, each assignment statement produces an error:

const pt = {}; // (A)
// Property 'x' does not exist on type '{}'
pt.x = 3; // Error
// Property 'y' does not exist on type '{}'
pt.y = 4; // Error
Copy the code

This is because the pt type in line A is inferred from its value {}, and you can only assign values to known attributes. How to solve this problem? Some readers may think of interfaces first, such as this:

interface Point {
  x: number;
  y: number;
}

// Type '{}' is missing the following 
// properties from type 'Point': x, y(2739)
const pt: Point = {}; // Error
pt.x = 3;
pt.y = 4;
Copy the code

Unfortunately, the TypeScript compiler still prompts errors for these scenarios. So how can this problem be solved? We can assign directly from object literals:

const pt = { 
  x: 3,
  y: 4};// OK
Copy the code

If you need to create objects step by step, you can use type assertion (AS) to eliminate TypeScript type checking:

const pt = {} as Point; 
pt.x = 3;
pt.y = 4; // OK
Copy the code

But it’s better to declare the variable’s type and build the object once:

const pt: Point = { 
  x: 3,
  y: 4};Copy the code

You may also encounter the following problems when using the object. assign method to merge multiple objects:

const pt = { x: Awesome!, y: 888 };
const id = { name: "semlinker" };
const namedPoint = {};
Object.assign(namedPoint, pt, id);

// Property 'name' does not exist on type '{}'.(2339)
namedPoint.name; // Error
Copy the code

At this point you can use the object expansion operator… To solve the above problems:

const pt = { x: Awesome!, y: 888 };
const id = { name: "semlinker" };
constnamedPoint = {... pt, ... id}//(property) name: string
namedPoint.name // Ok
Copy the code

Object literal types vs interface types

In addition to describing objects by their types and types, we can also describe objects by their properties:

// Object literal type
let obj3: { prop: boolean };

// Interface
interface ObjectType {
  prop: boolean;
}

let obj4: ObjectType;
Copy the code

There are two very similar ways to define object types in TypeScript:

// Object literal type
type ObjType1 = {
  a: boolean,
  b: number;
  c: string};// Interface
interface ObjType2 {
  a: boolean,
  b: number;
  c: string,}Copy the code

In the above code, we use semicolons or commas as separators. Trailing separators are allowed and optional. Okay, so now the question is, what’s the difference between object literal types and interface types? Here I will analyze the differences between them from the following aspects:

3.1 the inline

Object literal types can be inlined, whereas interfaces cannot:

// Inlined object literal type:
function f1(x: { prop: number }) {}

function f2(x: ObjectInterface) {} // referenced interface
interface ObjectInterface {
  prop: number;
}
Copy the code

3.2 Duplicate Names

Type aliases with duplicate names are illegal:

// @ts-ignore: Duplicate identifier 'PersonAlias'. (2300)
type PersonAlias = {first: string};

// @ts-ignore: Duplicate identifier 'PersonAlias'. (2300)
type PersonAlias = {last: string};
Copy the code

TypeScript 2.6 supports ignoring errors in.ts files by using // @ts-ignore at the top of an error line.

// the @ts-ignore comment ignores all errors in the next line. It is recommended in practice to add a hint after @ts-ignore explaining what errors were ignored.

Please note that this comment only hides errors, and we recommend that you use this comment sparingly.

Instead, interfaces with duplicate names will be merged:

interface PersonInterface {
  first: string;
}

interface PersonInterface {
  last: string;
}

const sem: PersonInterface = {
  first: 'Jiabao',
  last: 'Huang'};Copy the code

3.3 Mapping Types

For the mapping type (line A), we need to use the object literal type:

interface Point {
  x: number;
  y: number;
}

type PointCopy1 = {
  [Key in keyof Point]: Point[Key]; // (A)
};

// Syntax error:
// interface PointCopy2 {
// [Key in keyof Point]: Point[Key];
// };
Copy the code

3.4 Polymorphic this type

The polymorphic this type applies only to interfaces:

interface AddsStrings {
  add(str: string) :this;
};

class StringBuilder implements AddsStrings {
  result = ' ';
  add(str: string) {
    this.result += str;
    return this; }}Copy the code

Four,

The Object, Object, and {} types are also confusing to many new TypeScript readers. Because I don’t know what the difference is between them, when to use? In order to make readers more intuitive to understand the difference between them, we finally make a summary:

4.1 the object type

The object type is: a new type introduced in TypeScript 2.2 that represents non-primitive types.

// node_modules/typescript/lib/lib.es5.d.ts
interface ObjectConstructor {
  create(o: object | null) :any;
  // ...
}

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
Copy the code

4.2 the Object type

Object type: This is the type of all instances of the Object class. It is defined by the following two interfaces:

It is defined by the following two interfaces:

  • The Object interface defines properties on the Object. Prototype Object.
// node_modules/typescript/lib/lib.es5.d.ts

interface Object {
  constructor: Function;
  toString(): string;
  toLocaleString(): string;
  valueOf(): Object;
  hasOwnProperty(v: PropertyKey): boolean;
  isPrototypeOf(v: Object): boolean;
  propertyIsEnumerable(v: PropertyKey): boolean;
}
Copy the code
  • The ObjectConstructor interface defines the properties of the Object class.
// node_modules/typescript/lib/lib.es5.d.ts

interface ObjectConstructor {
  /** Invocation via `new` */
  new(value? :any) :Object;
  /** Invocation via function calls */(value? :any) :any;

  readonly prototype: Object;

  getPrototypeOf(o: any) :any;

  / /...
}

declare var Object: ObjectConstructor;
Copy the code

All instances of the Object class inherit all properties of the Object interface.

4.3 {} type

{} type: this describes an object that has no members. TypeScript generates a compile-time error when you try to access arbitrary properties of such an object.

// Type {}
const obj = {};

// Error: Property 'prop' does not exist on type '{}'.
obj.prop = "semlinker";
Copy the code

However, you can still use all the properties and methods defined on the Object type.

5. Reference resources

  • the-object-type-in-typescript
  • typing-objects-typescript
  • difference-between-object-and-in-typescript