Добрый вечер. Объясните, пожалуйста, подробнее про type predicates в TS. В каких случаях их можно использовать. Например, когда к одному обработчику можно применить
union typeи в нем задать, напримерTouchEvent | MouseEvent. Для чего использоватьinв type predicates? Спасибо.
Отвечает Игорь Антонов, автор курсов по JavaScript в Академии.
В JavaScript и TypeScript есть конструкции для проверки типа. Достаточно вспомнить ООП и оператор instanceof. Начнём немного издалека и посмотрим на такой код:
class Dog {}
class Cat {}
const keks = new Cat();
const buddy = new Dog();
function isCat(something) {
  if (something instanceof Cat) {
    return true;
  }
  return false;
}
Функция умеет отличать котов от собак, в ней объявлено два класса и функция isCat. Если аргументом передать keks, функция вернёт true. Для аргумента buddy результатом станет false. Проверка внутри функции реализована с помощью оператора instanceof.
Теперь вернёмся к TypeScript и взглянем на такой код:
type Dog = { 
  isBig: boolean;  name: string; 
}  
type Cat = {  
  lives: number;  name: string; 
}  
type Animal = Dog | Cat;  
function getAnimal(): Animal {
  return { isBig: true, name: "spike" };
}
Мы описали два типа (Dog и Cat), воспользовались типом объединения и вывели Animal. Ещё мы определи функцию getAnimal. Она позволяет получить объект для описания животного. Судя по форме объекта — это Dog.
💫 Узнайте больше о теории типов, научитесь на практике использовать аннотацию типов и обобщённое программирование на профессиональном курсе по TypeScript.
Пример
Давайте напишем код, который выведет в консоль значение поля lives для котов, и значение поля isBig для собак.
Сначала напишем функцию isCat. Чтобы в Animal отделить кошек от собак, проверим наличие того или иного поля. Если у есть поле lives. то значит это кот. Отразим это в функции. Она должна возвращать значение типа boolean.
Теоретически такая функция может выглядеть так:
function isCat(animal: Animal): boolean { 
  return (animal as Cat).lives !== undefined; 
} 
Одна, когда начнём ей пользоваться, получим ошибки, так как в animal могут быть и коты, и собаки. Попробуем добавить предикат. Установим его в качестве типа для возвращаемого значения функции:
function isCat(animal: Animal): animal is Cat { 
  return (animal as Cat).lives !== undefined; 
} 
animal is Cat — это предикат. Сигнатура строится из имени параметра функции animal и оператора is. Теперь попробуем воспользоваться этой функцией:
const animal = getAnimal();
if (isCat(animal)) {
  console.log(animal.lives);
} else {
  console.log(animal.isBig);
}
Если isCat вернёт true, то перед нами кот, соответственно, у него есть свойство lives. Попробуйте скопировать код в песочницу, а затем удалить имя свойства lives на строке, где происходит вывод в консоль. Поставьте точку и посмотрите список доступных полей. Вы увидите только lives и name. Это справедливо только для ветки if, так как TypeScript понимает, что если условие истинно, значит перед нами кот.
Попробуйте сделать то же самое внутри ветки else. В этот раз будут доступны только поля, которые есть у Dog. В контексте TS всё, что мы провернули, называется Type Guard.
Теперь по поводу in. Оператор тоже пригодится для сужения типа. Например, можно написать функцию isCat с его помощью:
function isCat(animal: Animal): animal is Cat {
  return 'lives' in animal;
}
Если в animal есть поле animal, значит это кот. Здесь, конечно, стоит добавить дополнительную проверку на undefined, но это уже детали.
Ещё немного о JavaScript