Interviewer: Tell me about type guards?

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', toUpperCasethe method is called; if it is 'number', toFixedthe 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, barkthe method is called; otherwise movethe 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 Shapethe type, which can be Circleeither or Rectangle. Through the custom predicate function isCircle, we judge shapewhether the type of the variable is Circleand 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 Vehicletypes which are Car, Bicycleand `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 inthe operator

inOperators 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 inthe operator to check 'radius'if the property exists shapein the object. If it exists, shapethe type of is narrowed Circle, and the corresponding code logic is executed. By using inthe 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 valuethe type of stringto 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 switchin the statement case, the TypeScript compiler will intelligently narrow down colorthe 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 truethe 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 valuethe result of the conditional expression is a truthy value (i.e. not a or the empty string) .nullvaluestring

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 isBirdthe predicate function to determine animalwhether the parameter is of Birdtype . In processAnimalthe function, by using the custom predicate function isBird, we can animalexecute the corresponding code logic according to the specific type of and narrow animalthe type range of within the code block.

Guess you like

Origin blog.csdn.net/weixin_52898349/article/details/132526711