A Discriminated union in TypeScript is used to discriminate between union types by having a property that holds a literal type as its value to help us identify the type of object we are working with.

Let us look at an example, to understand this concept better.

interface Developer {
  type: "developer";
  language: string;
}

interface DBA {
  type: "dba";
  database: string;
}

type Employee = Developer | DBA;

function getInfo(employee: Employee) {
  switch (employee.type) {
    case "developer":
      console.log(`Programming language to use: ${employee.language}`);
      break;
    case "dba":
      console.log(`Database to use: ${employee.database}`);
  }
}

let developer: Developer = {
  type: "developer",
  language: "Javascript",
};

let dba: DBA = {
  type: "dba",
  database: "MySQL",
};

getInfo(developer); // Programming language to use: Javascript
getInfo(dba); // Database to use: MySQL

In the above example, we have an interface Developer

interface Developer {
  type: "developer";
  language: string;
}

Here, we have a field type that discriminates this interface from others by specifying its value as developer. We can use this field to identify if an object belongs to the interface Developer whenever it is necessary to do so.

Similarly, we have another interface DBA that also has a type but the value is dba

interface DBA {
  type: "dba";
  database: string;
}

Next, we create a type Employee which is a union of Developer and DBA.

type Employee = Developer | DBA;

We have also created a function called getInfo(), which takes the object of type Employee as its parameter. This means the value passed to getInfo() can only be determined at runtime.

This creates an issue for TypeScript because the parameter passed can either be type Developer or DBA. Let’s say we pass an object of type Developer and in our function, we try to access the property database.

From the definition of Developer interface, we know that database property cannot be accessed by the object of type Developer. Hence, we need a way to differentiate object that is of type Developer from type DBA. This is where discriminated unions come into the picture.

function getInfo(employee: Employee) {
  switch (employee.type) {
    case "developer":
      console.log(`Programming language to use: ${employee.language}`);
      break;
    case "dba":
      console.log(`Database to use: ${employee.database}`);
  }
}

With the help of the discriminated union, we can simply check if employee.type === "developer" or if employee.type === "dba". This way we can prevent the code from breaking.