JS Tower
JavaScript & TypeScriptМодуль 8: Продвинутый TypeScript

Type Guards

Сужение типов в TypeScript

Цель урока

В этом уроке ты научишься:

  • Использовать встроенные type guards
  • Создавать пользовательские guards
  • Применять assertion functions

typeof

function process(value: string | number) {
  if (typeof value === "string") {
    console.log(value.toUpperCase()); // string
  } else {
    console.log(value.toFixed(2));    // number
  }
}

instanceof

class Dog {
  bark() { console.log("Гав!"); }
}

class Cat {
  meow() { console.log("Мяу!"); }
}

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

in

type Fish = { swim: () => void };
type Bird = { fly: () => void };

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

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

interface User {
  type: "user";
  name: string;
}

interface Admin {
  type: "admin";
  name: string;
  permissions: string[];
}

// Type guard функция
function isAdmin(person: User | Admin): person is Admin {
  return person.type === "admin";
}

function greet(person: User | Admin) {
  if (isAdmin(person)) {
    console.log("Админ: " + person.permissions.length + " прав");
  } else {
    console.log("Пользователь: " + person.name);
  }
}

Truthiness Narrowing

function printName(name: string | null | undefined) {
  if (name) {
    console.log(name.toUpperCase()); // string
  } else {
    console.log("Имя не указано");
  }
}

Equality Narrowing

function compare(a: string | number, b: string | boolean) {
  if (a === b) {
    // Оба должны быть string
    console.log(a.toUpperCase());
  }
}

Assertion Functions

function assertIsString(value: unknown): asserts value is string {
  if (typeof value !== "string") {
    throw new Error("Не строка!");
  }
}

function process(value: unknown) {
  assertIsString(value);
  // После assert TypeScript знает, что value — string
  console.log(value.toUpperCase());
}

Discriminated Unions

type Circle = { kind: "circle"; radius: number };
type Square = { kind: "square"; side: number };
type Shape = Circle | Square;

function getArea(shape: Shape): number {
  switch (shape.kind) {
    case "circle":
      return Math.PI * shape.radius ** 2;
    case "square":
      return shape.side ** 2;
  }
}

Практика

Задание 1: typeof guard

Задача: Создай функцию stringify.

Запустите код для проверки
Loading...
Ваш вывод:
Ожидаемый результат:
HELLO
42.00
yes

Задание 2: Пользовательский guard

Задача: Создай type guard для проверки массива.

Запустите код для проверки
Loading...
Ваш вывод:
Ожидаемый результат:
true
false

Задание 3: in guard

Задача: Различи типы по свойствам.

Запустите код для проверки
Loading...
Ваш вывод:
Ожидаемый результат:
4 колеса
2 колеса

Проверь себя

  1. Какие встроенные type guards есть?
  2. Как создать пользовательский guard?
  3. Что делает assertion function?