Сужение типов и уточняющие тайпгарды

  —  3 минуты

#theory#typescript#code
Читать статью в Telegram

Часто в TypeScript коде можно столкнуться с тем, что типы недостаточно точны. Это бывает в случаях, когда мы определяем типы, например, через unionstring | number и других подобных случаях.

Рассмотрим пример

typescript
1const value: string | number = ...
2
3parseInt(value) // ошибка
4

В таком случае мы получим TypeError , а всё потому что переменная value имеет более общий тип, чем тот, что ожидает parseInt — эта функция ожидает первым аргументом на вход только строку. В таком случае нам может помочь сужение типов, которое нам дают дополнительные проверки — тайпгарды.

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

Уточняющие тайпгарды — проверки, которые позволяют вывести более узкий тип из общего типа, например вывести string из типа string | number | boolean | null.

Определяющие же тайпгарды — проверки, которые позволяют вывести узкий тип из неизвестного типа, который в идеальном мире обозначается типом unknown

Есть несколько способов сузить тип

  1. Через оператор typeof
typescript
1const value: string | number = ...
2
3if (typeof value === 'string') {
4    // ошибки не будет, т.к.
5    // тип value в этом условии
6    // строго равняется "string"
7    parseInt(value)
8}
9
  1. Через ключевое слово in или метод hasOwnProperty
typescript
1const value: any[] | number = ...
2
3if ("map" in value) {
4    // value === array
5    value.map(...)
6}
7
8const animal = Cat | Dog = ...
9
10if (animal.hasOwnProperty('meow')) {
11    // animal === Cat
12    animal.meow()
13}
14
  1. Через ключевое слово instanceof
typescript
1const person: Fireman | Programmer = ...
2
3// при условии что Programmer - класс
4// а person - инстанс класса
5if (person instanceof Programmer) {
6    person.code()
7}
8
  1. Через встроенные в язык функции проверки типа
typescript
1const value: any[] | number = ...
2
3if (Array.isArray(value)) {
4    value.map(...)
5}
6
  1. Через existed check, например, для null и undefined
typescript
1const user: User | null = ...
2
3if (user) {
4    console.log(user.name)
5}
6

Также подобные проверки можно выносить в отдельные функции, которые обычно именуются по следующему шаблону — is + <название типа или интерфейса>, например, для типа User будет логично создать тайпгард isUser, вот небольшой пример:

typescript
1type Account = User | Admin | Manager
2
3const isUser = (account: Account): account is User => {
4    return account.type === "user"
5}
6

В данном случае мы используем предикат для пользователя. Вообще, предикат — это базовый математический термин, но если кто-то не знал или подзабыл, также напоминаю:

В нашем контексте, предикат — уникальное определяющее свойство сущности, относительно которого можно быть точно уверенным в её идентичности.

Если более просто, то мы знаем, что если account.type === “user”, то перед нами ни что иное как User. В этом случае тип аккаунта является предикатом, на основе которого можно делать выводы о типе аккаунта без дополнительных проверок.

И всё это касается лишь уточняющих тайпгардов, пост об определяющих тайпгардах можно прочитать в отдельной статье

Статья была полезной?