Advanced types - intersection types, type Guard,Discriminated union

  1. intersection types “&”

    intersection types allow us to combine other types. Use & to combine types. The combined type contains all properties and methods of those combined types.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    type Admin = {
    name: string;
    privileges: string[];
    };
    type Employee = {
    name: string;
    startDate: Date;
    }
    type ElevatedEmployee = Admin & Employee; //This type contains all properties and methods of Admin and Employee.

    intersection types are closely related to interface inheritance because the same functionality can be achieved using interfaces.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    interface Admin {
    name: string;
    privileges: string[];
    };
    interface Employee {
    name: string;
    startDate: Date;
    }
    interface ElevatedEmployee extends Admin, Employee {};

    When combining multiple union types using intersection types, the intersection (common part) is obtained.

    1
    2
    3
    type Combinable = string | number;
    type Numeric = number | boolean;
    type Universal = Combinable & Numeric; //type Universal = number

  2. Type Gaurd type protection

    Type protection is used to converge types, that is, to converge variables that may have multiple types into an exact type. Type guarding is a term that describes detecting the existence of a property or method before trying to use it.

    1. typeof type protection (used to detect non-object)

      typeof can only compare types known to JS, namely object, string, number, and boolean. TS custom types cannot be compared.

      1
      2
      3
      4
      5
      6
      7
      type Combinable = string | number;
      function add(a: Combinable, b: Combinable) {
      if (typeof a === "string" || typeof b === "string") {
      return a.toString() + b.toString();
      }
      return a + b;
      }

    1. in type protection (used to detect object)

      After two objects are unioned, it is impossible to use typeof to determine whether they contain a certain attribute, because the type is object (typeof emp === "object"). There is also no way to detect whether type is a custom type (typeof emp === "Employee"), because JS does not know the custom type. This is because typeof uses JS instead of TS at runtime, so it can only compare the type obtained by typeof with the type known to JS.

      The in keyword can check for prohibited attributes in TS.

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      interface Admin {
      name: string;
      privileges: string[];
      };
      interface Employee {
      name: string;
      startDate: Date;
      }
      type UnknownEmployee = Employee | Admin;
      function printEmp (emp: UnknownEmployee) {
      if("privileges" in emp){ ... }; //Check whether privileges is an attribute on emp.
      }

    1. Type protection of instanceof (used to detect object, only for instances of class, cannot be used for interface)

      The function implemented by the type protection of in is similar to that of in, but more elegant. Used to detect whether an object is an instance of a class. If it is an interface, instanceof cannot be used to detect it.

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      class Car {
      drive(){
      console.log("Driving")
      }
      }
      class Truck {
      drive(){
      console.log("Driving")
      }
      loadCargo(amount: number) {
      console.log("Loading cargo ..." + amount);
      }
      }
      type Vehicle = Car | Truck; //[Note]
      const v1 = new Car();
      const v2 = new Truck();

      function useVehicle(vehicle: Vehicle) {
      vehicle.drive(); //Shared methods
      if (vehicle instanceof Truck) { //Detect instance
      vehicle.loadCargo(1000);
      }
      }

      Note: Car and Truck are two classes, but in TypeScript, a class is also a type, just like basic data types (such as string and number) and other custom types . Therefore, we can combine the Car and Truck types into a union type Vehicle.


  3. Discriminated Unions Discriminated Unions

    Discriminated Unions is a design pattern that can be used when working with union types to make it easier to implement type protection. It is used for the type of object.

    A type resulting from the union of two different types, which usually have some related but different properties or methods. Discriminated Unions means that in each object (type) that makes up the union, there is a public property that describes the object. So we can use this public property in type detection to achieve 100% type safety.

    Not using Discriminated Unions:

    1
    2
    3
    4
    5
    6
    7
    interface Bird {
    flyingSpeed: number;
    }
    interface Horse {
    runningSpeed: number;
    }
    type Animal = Bird | Horse;

    Use Discriminated Unions:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    interface Bird {
    type: "bird";
    flyingSpeed: number;
    }
    interface Horse {
    type: "horse";
    runningSpeed: number;
    }
    type Animal = Bird | Horse;
    function moveAnimal(animal: Animal) {
    let speed;
    switch (animal.type) {
    case "bird": speed = animal.flyingSpeed; break;
    case "horse": speed = animal.runningSpeed; break;
    }
    }

Share