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
9type 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
9interface 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
3type Combinable = string | number;
type Numeric = number | boolean;
type Universal = Combinable & Numeric; //type Universal = number
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.
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
7type Combinable = string | number;
function add(a: Combinable, b: Combinable) {
if (typeof a === "string" || typeof b === "string") {
return a.toString() + b.toString();
}
return a + b;
}
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
12interface 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.
}
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
23class 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
andTruck
are two classes, but in TypeScript, a class is also a type, just like basic data types (such asstring
andnumber
) and other custom types . Therefore, we can combine theCar
andTruck
types into a union typeVehicle
.
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
7interface 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
16interface 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;
}
}