Тип guards

Модуль 2: Fullstack
6 уровень , 6 лекция
Открыта

7.1 Основы Type Guards

Type Guards в TypeScript — это специальные конструкции, которые позволяют проверять типы значений во время выполнения. Они обеспечивают дополнительный уровень безопасности и помогают избежать ошибок, связанных с некорректным использованием типов.

Type Guards позволяют нам уточнять типы переменных в блоках кода, используя различные проверки. Это особенно полезно в тех случаях, когда переменная может иметь несколько типов, например, при использовании union types.

Рассмотрим простейший пример использования Type Guards:

JavaScript
    
      "use strict";
      function isString(value) {
        return typeof value === "string";
      }
      function example(value) {
        if (isString(value)) {
          console.log(value.toUpperCase()); // TypeScript знает, что value — это string
        }
        else {
          console.log(value.toFixed(2)); // TypeScript знает, что value — это number
        }
      }
      example("hello"); // Вывод: HELLO
      example(123.456); // Вывод: 123.46
    
  
TypeScript
    
      function isString(value: any): value is string {
        return typeof value === "string";
      }

      function example(value: string | number) {
        if (isString(value)) {
          console.log(value.toUpperCase()); // TypeScript знает, что value — это string
        } else {
          console.log(value.toFixed(2)); // TypeScript знает, что value — это number
        }
      }

      example("hello"); // Вывод: HELLO
      example(123.456); // Вывод: 123.46
    
  

Функция isString — это Type Guard, которая проверяет, является ли переданное значение строкой. Внутри функции example, после вызова isString, TypeScript точно знает тип переменной value и позволяет использовать методы, доступные только для этого типа.

Типы Type Guards

Существует несколько типов Type Guards, которые можно использовать в TypeScript:

  • пользовательские Type Guards
  • typeof
  • instanceof
  • in

8.2 Пользовательские Type Guards

Пользовательские Type Guards позволяют создавать функции, которые проверяют типы значений и возвращают типизированные утверждения.

Пример:

JavaScript
    
      "use strict";
      function isCat(animal) {
          return animal.meow !== undefined;
      }
      function makeSound(animal) {
          if (isCat(animal)) {
              animal.meow();
          }
          else {
              animal.bark();
          }
      }
      const myCat = { meow: () => console.log("Meow") };
      const myDog = { bark: () => console.log("Bark") };
      makeSound(myCat); // Вывод: Meow
      makeSound(myDog); // Вывод: Bark
    
  
TypeScript
    
      interface Cat {
        meow(): void;
      }

      interface Dog {
        bark(): void;
      }

      function isCat(animal: Cat | Dog): animal is Cat {
        return (animal as Cat).meow !== undefined;
      }

      function makeSound(animal: Cat | Dog) {
        if (isCat(animal)) {
          animal.meow();
        } else {
          animal.bark();
        }
      }

      const myCat: Cat = { meow: () => console.log("Meow") };
      const myDog: Dog = { bark: () => console.log("Bark") };

      makeSound(myCat); // Вывод: Meow
      makeSound(myDog); // Вывод: Bark
    
  

Здесь функция isCat проверяет, является ли животное кошкой, путем проверки наличия метода meow. Внутри функции makeSound TypeScript использует результат isCat для уточнения типа животного.

8.3 typeof

TypeScript предоставляет встроенный оператор typeof, который позволяет проверять тип примитивных значений (строк, чисел, boolean и т. д.).

Пример:

JavaScript
    
      "use strict";
      function example(value) {
          if (typeof value === "string") {
              console.log(value.toUpperCase()); // TypeScript знает, что value — это string
          }
          else {
              console.log(value.toFixed(2)); // TypeScript знает, что value — это number
          }
      }
      example("hello"); // Вывод: HELLO
      example(123.456); // Вывод: 123.46
    
  
TypeScript
    
      function example(value: string | number) {
        if (typeof value === "string") {
          console.log(value.toUpperCase()); // TypeScript знает, что value — это string
        } else {
          console.log(value.toFixed(2)); // TypeScript знает, что value — это number
        }
      }

      example("hello"); // Вывод: HELLO
      example(123.456); // Вывод: 123.46
    
  

В этом примере оператор typeof используется для проверки типа переменной value.

8.4 instanceof

Оператор instanceof используется для проверки того, является ли объект экземпляром определенного класса или его подкласса.

Пример:

JavaScript
    
      "use strict";
      class Cat {
          meow() {
              console.log("Meow");
          }
      }
      class Dog {
          bark() {
              console.log("Bark");
          }
      }
      function makeSound(animal) {
          if (animal instanceof Cat) {
              animal.meow();
          }
          else {
              animal.bark();
          }
      }
      const myCat = new Cat();
      const myDog = new Dog();
      makeSound(myCat); // Вывод: Meow
      makeSound(myDog); // Вывод: Bark
    
  
TypeScript
    
      class Cat {
        meow() {
          console.log("Meow");
        }
      }

      class Dog {
        bark() {
          console.log("Bark");
        }
      }

      function makeSound(animal: Cat | Dog) {
        if (animal instanceof Cat) {
          animal.meow();
        } else {
          animal.bark();
        }
      }

      const myCat = new Cat();
      const myDog = new Dog();

      makeSound(myCat); // Вывод: Meow
      makeSound(myDog); // Вывод: Bark
    
  

В этом примере оператор instanceof используется для проверки того, является ли объект экземпляром класса Cat.

8.5 in

Оператор in используется для проверки наличия свойства в объекте. Это полезно для проверки типов объектов, которые имеют разные свойства.

Пример:

JavaScript
    
      "use strict";
      function getAnimal() {
          return Math.random() > 0.5
              ? {
                  fly: () => console.log("Bird is flying"),
                  layEggs: () => console.log("Bird is laying eggs")
              }
              : {
                  swim: () => console.log("Fish is swimming"),
                  layEggs: () => console.log("Fish is laying eggs")
              };
      }
      function move(animal) {
          if ("fly" in animal) {
              animal.fly();
          }
          else {
              animal.swim();
          }
      }
      const animal = getAnimal();
      move(animal); // В зависимости от случайного выбора, либо "Bird is flying", либо "Fish is swimming"
    
  
TypeScript
    
      interface Bird {
        fly(): void;
        layEggs(): void;
      }

      interface Fish {
        swim(): void;
        layEggs(): void;
      }

      function getAnimal(): Bird | Fish {
        return Math.random() > 0.5
          ? {
              fly: () => console.log("Bird is flying"),
              layEggs: () => console.log("Bird is laying eggs")
            }
          : {
              swim: () => console.log("Fish is swimming"),
              layEggs: () => console.log("Fish is laying eggs")
            };
      }

      function move(animal: Bird | Fish) {
        if ("fly" in animal) {
          animal.fly();
        } else {
          animal.swim();
        }
      }

      const animal = getAnimal();
      move(animal); // В зависимости от случайного выбора, либо "Bird is flying", либо "Fish is swimming"
    
  

Оператор in в этом примере используется для проверки наличия свойства fly в объекте animal. Так как функция getAnimal() возвращает объект, который может быть либо типа Bird, либо типа Fish, нам нужно определить, с каким конкретно объектом мы имеем дело, чтобы правильно вызвать его метод.

Конкретно в этом случае if ("fly" in animal) проверяет, есть ли у объекта animal свойство fly. Если это так, значит animal — птица (Bird), и мы можем вызвать метод fly(). Если такого свойства нет, значит, это рыба (Fish), и вызывается метод swim().

Таким образом, оператор in позволяет определить тип объекта на основе наличия конкретного метода, что важно для корректного выполнения соответствующей логики для разных типов животных.

8.6 Реальные примеры

Type Guards используются в реальных проектах для обеспечения типовой безопасности и предотвращения ошибок.

Пример 1: Обработка различных типов данных

JavaScript
    
      "use strict";
      function processData(data) {
        if (typeof data === "string") {
          console.log(`String data: ${data.toUpperCase()}`);
        }
        else if (typeof data === "number") {
          console.log(`Number data: ${data.toFixed(2)}`);
        }
        else {
          console.log(`Boolean data: ${data ? "True" : "False"}`);
        }
      }
      processData("hello"); // Вывод: String data: HELLO
      processData(123.456); // Вывод: Number data: 123.46
      processData(true); // Вывод: Boolean data: True
    
  
TypeScript
    
      type Data = string | number | boolean;

      function processData(data: Data) {
        if (typeof data === "string") {
          console.log(`String data: ${data.toUpperCase()}`);
        } else if (typeof data === "number") {
          console.log(`Number data: ${data.toFixed(2)}`);
        } else {
          console.log(`Boolean data: ${data ? "True" : "False"}`);
        }
      }

      processData("hello"); // Вывод: String data: HELLO
      processData(123.456); // Вывод: Number data: 123.46
      processData(true); // Вывод: Boolean data: True
    
  

Пример 2: Работа с различными типами сообщений

JavaScript
    
      "use strict";
      function handleMessage(message) {
          switch (message.type) {
              case "text":
                  console.log(`Text message: ${message.text}`);
                  break;
              case "image":
                  console.log(`Image message: ${message.url}`);
                  break;
              case "video":
                  console.log(`Video message: ${message.url}, duration: ${message.duration}`);
                  break;
          }
      }
      handleMessage({ type: "text", text: "Hello, world!" }); // Вывод: Text message: Hello, world!
      handleMessage({ type: "image", url: "http://example.com/image.jpg" }); // Вывод: Image message: http://example.com/image.jpg
      handleMessage({ type: "video", url: "http://example.com/video.mp4", duration: 120 }); // Вывод: Video message: http://example.com/video.mp4, duration: 120
    
  
TypeScript
    
      interface TextMessage {
        type: "text";
        text: string;
      }

      interface ImageMessage {
        type: "image";
        url: string;
      }

      interface VideoMessage {
        type: "video";
        url: string;
        duration: number;
      }

      type Message = TextMessage | ImageMessage | VideoMessage;

      function handleMessage(message: Message) {
        switch (message.type) {
          case "text":
            console.log(`Text message: ${message.text}`);
            break;
          case "image":
            console.log(`Image message: ${message.url}`);
            break;
          case "video":
            console.log(`Video message: ${message.url}, duration: ${message.duration}`);
            break;
        }
      }

      handleMessage({ type: "text", text: "Hello, world!" }); // Вывод: Text message: Hello, world!
      handleMessage({ type: "image", url: "http://example.com/image.jpg" }); // Вывод: Image message: http://example.com/image.jpg
      handleMessage({ type: "video", url: "http://example.com/video.mp4", duration: 120 }); // Вывод: Video message: http://example.com/video.mp4, duration: 120
    
  

Преимущества использования Type Guards

  1. Повышенная типовая безопасность: обеспечивают строгую типизацию, помогая предотвратить ошибки на этапе компиляции.
  2. Гибкость: позволяют обрабатывать значения с различными типами, улучшая гибкость кода.
  3. Улучшенная читаемость и поддерживаемость: явное указание проверок типов делает код более понятным и легким для поддержки.
  4. Предотвращение ошибок времени выполнения: проверка типов во время выполнения помогает избежать ошибок, связанных с некорректным использованием типов.
3
Задача
Модуль 2: Fullstack, 6 уровень, 6 лекция
Недоступна
Проверка типа через typeof
Проверка типа через typeof
3
Задача
Модуль 2: Fullstack, 6 уровень, 6 лекция
Недоступна
Проверка типа через instanceof
Проверка типа через instanceof
3
Задача
Модуль 2: Fullstack, 6 уровень, 6 лекция
Недоступна
Пользовательский Type Guard
Пользовательский Type Guard
3
Задача
Модуль 2: Fullstack, 6 уровень, 6 лекция
Недоступна
Проверка свойств через in
Проверка свойств через in
Комментарии (1)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Константин Уровень 55
14 июля 2025
Что за кринжовый пример в пункте 8.5? Зачем здесь Math.random(), вопрос, двоеточие? 👎