In TypeScript, union and intersection types serve as powerful tools to enrich the flexibility and robustness of type definitions.
Table of content:
Union:
Union types allow us to define a variable that can hold values of multiple types. Previously, we could only specify a single type for a variable:
type value = string;
Union types are usefull when a variable needs to handle different types of values, which make type definitions more flexible
type value = string | number;
Consider the following example:
const logMessage = (value): void => {
if (typeof value === "string")
console.log(`string parameter = ${value}`);
else if (typeof value === "number")
console.log(`number parameter = ${value}`);
};
In logMessage function, the value
parameter is accepted without any type definition, which lead an implicit assignment as any
type, means it will accept anything.
For example, consider a scenario where we want that value
will only accept number
or string
here in that case we can use union. syntax like value: string | number
which mean that it will accept only string
or number
, if we define like value: string | number
it only accept string
, number
or boolean
. Let's apply it in previous example.
const logMessage = (value: string | number): void => {
if (typeof value === "string")
return console.log(`string parameter = ${value}`);
console.log(`number parameter = ${value}`);
};
const logMessage = (value: string | number | boolean): void => {
if (typeof value === "string")
return console.log(`string parameter = ${value}`);
else if (typeof value === "number")
return console.log(`number parameter = ${value}`);
console.log(`boolean parameter = ${value}`);
};
Also beside types we can define what value it can contain like for example gender. There are two gender "male" and "female", so if someone give anything else then we don't want to accept it.
type User = {
id: string;
name: string;
age: number;
gender: string;
};
const user1: User = {
id: "1",
name: "John Doe",
age: 20,
gender: "male",
};
const user2: User = {
id: "2",
name: "Alice Smith",
age: 22,
gender: "shorifa", // something else without "male" or female
};
In above example we only want to gender value as "male" or "female" not anything else like "shorif", "shorifa" so we also can mention that what gender can contain like below.
type User = {
id: string;
name: string;
age: number;
gender: "male" | "female";
};
const user1: User = {
id: "1",
name: "John Doe",
age: 20,
gender: "male",
};
const user2: User = {
id: "2",
name: "Alice Smith",
age: 22,
gender: "shorifa", // Error: Type "shorifa" is not assignable to type '"male" | "female"'
};
Now take a look at another example:
interface Developer {
name: string;
role: string;
programmingLanguages: string[];
experienceYears: number;
}
interface Designer {
name: string;
role: string;
designTools: string[];
experienceYears: number;
}
type Employee = Developer | Designer;
const employee1: Employee = {
name: "Bob Johnson",
role: "Front-end developer",
programmingLanguages: ["JavaScript", "TypeScript"],
experienceYears: 5,
};
const employee2: Employee = {
name: "Alice Smith",
role: "UI Designer",
designTools: ["Photoshop", "Figma"],
experienceYears: 5,
};
In this example, we have two interfaces: Developer
and Designer
. We want to create a new type called Employee
that represents employees who can be either developers or designers. We achieve this using a union type.
Here employee1, employee2 are of type Employee
and employee1
is developer and employee2
id designer.
This use of union types demonstrates how TypeScript empowers developer to create versatile and expressive type definitions so easily.
Intersection:
Intersection is denoted by &
, I is used to combine multiple interface or types to create new type.
let's understand intersection through an example.
interface FrontendDeveloper {
id: string;
name: string;
frontendSkills: string[];
}
interface BackendDeveloper {
id: string;
name: string;
backendSkills: string[];
}
type FullstackDeveloper = FrontendDeveloper & BackendDeveloper;
const developer1: FullstackDeveloper = {
id: "1",
name: "Michael Johnson",
frontendSkills: ["HTML", "CSS", "JavaScript", "React", "Redux"],
backendSkills: ["Nodejs", "Express", "Mongodb", "PostgreSQL"],
};
Here in this example we have two interface named FrontendDeveloper
, BackendDeveloper
, we combined both to create type FullstackDeveloper
and after comining them its type will look alike ---
type FullstackDeveloper = {
id: string;
name: string;
frontendSkills: string[];
backendSkills: string[];
};
where frontendSkills
came from FrontendDeveloper
and backendSkills
came from BackendDeveloper
.
Let's see one more example using type
instead of interface
:
type People = {
name: string;
age: number;
};
type Employee = {
id: string;
companyName: string;
salary: number;
designation: string;
};
type EmployeeFullDetails = People & Employee;
const employee1: EmployeeFullDetails = {
name: "John Doe",
age: 30,
id: "123456",
companyName: "TechCorp",
salary: 80000,
designation: "Senior Software Engineer",
};
Similarly, we define a new type EmployeeFullDetails
by intersecting the People
and Employee
types. This allows EmployeeFullDetails
to contain properties from both types, providing a comprehensive representation of an employee's details.
Union and intersection types are valuable features in TypeScript, offering developers powerful tools to create flexible and robust type definitions. By using these features, developers can write more expressive and error-resistant code, leading to improved software quality and developer productivity.