type guard
overview
In TypeScript, type guards can be used to check the type of a variable at runtime and to narrow the type of a variable to a more specific type inside a code block. This type narrowing allows the TypeScript compiler to better understand the intent of our code, providing more accurate type inference and type checking.
Type guards usually use type assertion, type predicate, typeof operator, instanceof operator or custom predicate function to determine the specific type of the variable, and narrow the type range of the variable according to the result of the determination.
typeof type guard
typeof type guards allow us to use the typeof operator to perform conditional judgments in code based on the type range of a variable.
function printValue(value: string | number) {
if (typeof value === 'string') {
console.log(value.toUpperCase());
} else {
console.log(value.toFixed(2));
}
}
printValue('hello'); // 输出: HELLO
printValue(3.1415); // 输出: 3.14
In the above example, we used the typeof operator to check the type of the variable in the conditional statement value
. If its type is 'string'
, toUpperCase
the method is called; if it is 'number'
, toFixed
the method is called. By using typeof type guards, we can execute different code logic according to different types.
instanceof type guard
The instanceof type guard allows us to use the instanceof operator to check the type of an object and narrow down the type of an object inside a code block.
class Animal {
move() {
console.log('Animal is moving');
}
}
class Dog extends Animal {
bark() {
console.log('Dog is barking');
}
}
function performAction(animal: Animal) {
if (animal instanceof Dog) {
animal.bark();
} else {
animal.move();
}
}
const animal1 = new Animal();
const animal2 = new Dog();
performAction(animal1); // 输出: Animal is moving
performAction(animal2); // 输出: Dog is barking
In the above example, we used the instanceof operator to check the type of the variable in the conditional statement animal
. If it is an instance of the Dog class, bark
the method is called; otherwise move
the method is called. By using instanceof type guards, we can perform different operations based on the specific type of the object
Same code logic.
Use custom predicate function type guards
Custom predicate function type guards allow us to define our own functions to judge the type of variables based on certain conditions, and narrow the type range of variables inside the code block.
interface Circle {
kind: 'circle';
radius: number;
}
interface Rectangle {
kind: 'rectangle';
width: number;
height: number;
}
type Shape = Circle | Rectangle;
function calculateArea(shape: Shape) {
if (isCircle(shape)) {
console.log(Math.PI * shape.radius ** 2);
} else {
console.log(shape.width * shape.height);
}
}
function isCircle(shape: Shape): shape is Circle {
return shape.kind === 'circle';
}
const circle: Circle = {
kind: 'circle', radius: 5 };
const rectangle: Rectangle = {
kind: 'rectangle', width: 10, height: 20 };
calculateArea(circle); // 输出: 78.53981633974483
calculateArea(rectangle); // 输出: 200
In the example above, we defined Shape
the type, which can be Circle
either or Rectangle
. Through the custom predicate function isCircle
, we judge shape
whether the type of the variable is Circle
and narrow the type range of the variable inside the conditional statement. By using custom predicate function type guards, we can execute corresponding code logic based on specific predicate conditions.
joint type guard
Type guards are most commonly used in union types, which may contain several different type options.
interface Car {
type: 'car';
brand: string;
wheels: number;
}
interface Bicycle {
type: 'bicycle';
color: string;
}
interface Motorcycle {
type: 'motorcycle';
engine: number;
}
type Vehicle = Car | Bicycle | Motorcycle;
function printVehicleInfo(vehicle: Vehicle) {
switch (vehicle.type) {
case 'car':
console.log(`Brand: ${
vehicle.brand}, Wheels: ${
vehicle.wheels}`);
break;
case 'bicycle':
console.log(`Color: ${
vehicle.color}`);
break;
case 'motorcycle':
console.log(`Engine: ${
vehicle.engine}`);
break;
default:
const _exhaustiveCheck: never = vehicle;
}
}
const car: Car = {
type: 'car', brand: 'Toyota', wheels: 4 };
const bicycle: Bicycle = {
type: 'bicycle', color: 'red' };
const motorcycle: Motorcycle = {
type: 'motorcycle', engine: 1000 };
printVehicleInfo(car); // 输出: Brand: Toyota, Wheels: 4
printVehicleInfo(bicycle); // 输出: Color: red
printVehicleInfo(motorcycle); // 输出: Engine: 1000
In the example above, we defined Vehicle
types which are Car
, Bicycle
and `Motor
cycle 的联合类型。通过使用
switch 语句和根据
vehicle.type 的不同值进行类型守卫,我们可以在每个
case 分支中收窄
vehicle` type range, and execute the corresponding code logic. In this way, we are able to more accurately infer and check variables of union types.
Type guards using in
the operator
in
Operators can be used in TypeScript to determine whether a property exists in an object, thereby performing type judgment and type narrowing.
interface Circle {
kind: 'circle';
radius: number;
}
interface Rectangle {
kind: 'rectangle';
width: number;
height: number;
}
type Shape = Circle | Rectangle;
function printArea(shape: Shape) {
if ('radius' in shape) {
console.log(Math.PI * shape.radius ** 2);
} else {
console.log(shape.width * shape.height);
}
}
const circle: Circle = {
kind: 'circle', radius: 5 };
const rectangle: Rectangle = {
kind: 'rectangle', width: 10, height: 20 };
printArea(circle); // 输出: 78.53981633974483
printArea(rectangle); // 输出: 200
In the above example, we used in
the operator to check 'radius'
if the property exists shape
in the object. If it exists, shape
the type of is narrowed Circle
, and the corresponding code logic is executed. By using in
the operator for type judgment, we can perform type narrowing based on the presence or absence of attributes.
Control Flow Type Guards
In TypeScript, when certain operations are performed, the compiler intelligently adjusts the type range of variables, which is called control flow type narrowing.
Conditional judgment of if statement
function printValue(value: string | number) {
if (typeof value === 'string') {
console.log(value.toUpperCase());
} else {
console.log(value.toFixed(2));
}
}
In the above example, when typeof value === 'string'
the conditional judgment of is executed, the TypeScript compiler will narrow value
the type of string
to provide corresponding intellisense hints and type checks inside the code block.
Case judgment of switch statement
type Fruit = 'apple' | 'banana' | 'orange';
function getFruitColor(fruit: Fruit) {
let color: string;
switch (fruit) {
case 'apple':
color = 'red';
break;
case 'banana':
color = 'yellow';
break;
default:
color = 'orange';
}
console.log(`The color of ${
fruit} is ${
color}`);
}
In the above example, according to the judgment switch
in the statement case
, the TypeScript compiler will intelligently narrow down color
the type of to the corresponding color string.
Truth type guards
Truth narrowing is a mechanism for type narrowing in conditional expressions. TypeScript compiles when conditional expression evaluates to true
The filter will narrow the type of the variable to true
the type of .
function processValue(value: string | null) {
if (value) {
console.log(value.toUpperCase());
} else {
console.log('Value is null or empty');
}
}
In the example above, the TypeScript compiler will narrow the type of to when value
the result of the conditional expression is a truthy value (i.e. not a or the empty string) .null
value
string
Custom type judgment (Type Predicates) guard
TypeScript provides the function of custom type judgment, which allows us to define our own predicate function for type judgment and type narrowing.
interface Bird {
fly(): void;
}
interface Fish {
swim(): void;
}
function isBird(animal: Bird | Fish): animal is Bird {
return (animal as Bird).fly !== undefined;
}
function processAnimal(animal: Bird | Fish) {
if (isBird(animal)) {
animal.fly();
} else {
animal.swim();
}
}
In the above example, we defined isBird
the predicate function to determine animal
whether the parameter is of Bird
type . In processAnimal
the function, by using the custom predicate function isBird
, we can animal
execute the corresponding code logic according to the specific type of and narrow animal
the type range of within the code block.