Как стать автором
Обновить

TypeScript и все что тебе нужно в разработке

Уровень сложностиСредний
Время на прочтение6 мин
Количество просмотров30K

АвторМаслов Андрей, Front-end разработчик.

О статье

Эта статья создана для облегчения процесса изучения TypeScript с помощью практичных примеров. Более подробную информацию можно найти в документации или в дополнительных материалах.

Статья предназначена как для начинающих разработчиков, которые только начинают знакомиться с TypeScript, так и для опытных разработчиков, желающих углубить свои знания в этом языке. Здесь вы найдете краткое и информативное изложение ключевых аспектов TypeScript, которые могут быть полезными в повседневной разработке. Для вашего удобства, оглавление статьи содержит ссылки на конкретные темы TypeScript, так что вы можете быстро перейти к интересующей вас части материала.

Навигатор

Intersection Types и Union Types

Intersection Types

В TS вы можете пересекать типы. Вы можете получить тип C способом пересечения типов А и В. Смотрите пример ниже:

type A = {
  id: number
  firstName: string
  lastName: string 
}

type B = {
  id: number
  height: number 
  weight: number
}

type C = A & B 

//Итог пересечения типов A и B
type C = {
  id: number
  firstName: string
  lastName: string 
  height: number 
  weight: number
}

Union Types

Аналогично пересечению, вы можете выполнить и объединение типов, т.е создать аннотации несколько типов в текущей переменной. Смотрите пример ниже:

type A = number 
type B = string 
type C = A | B

//Итог объединения A и B
type C = number или string

const parseAmount = (val: C) => {
  if (typeof val === 'number') {
    return val 
  }

  if (typeof val === 'string') {
    return val.resplace(',', '.')
  }
}

Generic Types

Дженерики значительно расширяют возможности TypeScript в повседневном программировании, позволяя нам эффективно переиспользовать типы, избегая создания множества аналогичных "клонов". Давайте рассмотрим это на практическом примере: создание типа, способного корректно обрабатывать ответы методов.

type FetchResponse<T> = {
  data: T
  errorMessage: string
  errorCode: number
}

type AuthDataRs = {
  accessToken: string 
  refreshToken: string 
}

const login = async (lg: string, ps: string): FetchResponse<AuthDataRs> => {
  const response = await fetch(...)

  return response
}

//FetchResponse<AuthDataRs> - вот такая запись позволит вам 
//переиспользовать FetchResponse для различных запросов.

При необходимости можно расширять свой тип несколькими дженериками:

type FetchResponse<T, P> = {
  data: T
  error: P
}

Так же вы можете назначать тип по умолчанию дженерику:

type FetchResponse<T, P = string> = {
  data: T
  error: P
}

Если дженерику не назначен явный тип по умолчанию, то вам необходимо обязательно указывать тип при его использовании. В случае, если у дженерика есть дефолтное значение, передача типа может быть опциональной или даже вовсе не требоваться.

Utility Types

Это утилиты, которые предназначены для удобной работы, а именно генерации новых типов на основе других.

Awaited

Awaited<T>
Утилита предназначена для ожидания в асинхронных операциях, например:

type A = Awaited<Promise<number>>;
//type A -> number

Partial

Partial<T>
Утилита предназначена для создания нового типа, где каждое свойство станет опциональным. Напомню, для того чтобы сделать свойство объекта опциональным, необходимо использовать знак "?":

type A = {
  id: number 
  name?: string //Опциональное свойство (необязательное)
}

Как работает Partial ?

type A = {
  id: number 
  name: string
}

type B = Partial<A>

//Output
type B = {
  id?: number //Опциональное свойство (необязательное)
  name?: number //Опциональное свойство (необязательное)
}

Required

Required<T>
Утилита работает в точности наоборот как Partial. Свойства текущего типа делает строго обязательными.

type A = {
  id?: number 
  name?: string
}

type B = Required<A>

//Output
type B = {
  id: number //Обязательное свойство
  name: number //Обязательное свойство
}

Readonly

Readonly<T>
Утилиты преобразует все свойства типа, делает их недоступными для переназначения с использованием нового значения.

type A = {
  id: number
  name: string
}

type B = Readonly<A>

const firstObj: A = { id: 0, name: 'first'}
const secondObj: B = { id: 1, name: 'second'}

firstObj.name = 'first_1' // it's correct
secondObj.name = 'second_2' //Cannot assign to 'name' because it is a read-only property.

Если у вас есть необходимость сделать поле readonly только для определенного свойства объекта, то необходимо написать ключевое слово перед именем св-ва:

type A = {
  readonly id: number 
  name: string
}

Record

Record<T, U>
Утилита предназначена для создания типа объекта, Record<Keys, Types>, где Keys - имена свойств объекта, а Types - типы значений свойств.

enum CarNames {
  AUDI = 'audi',
  BMW = 'bmw'
}

type CarInfo = {
  color: string 
  price: number 
}

type Cars = Record<CarNames, CarInfo>

//Output
type Cars = {
  audi: CarInfo;
  bmw: CarInfo;
}

Pick

Pick<T, 'key1' | 'key2'>
Утилита предназначена для создания нового типа из выбранных свойств объекта.

type A = {
  id: number 
  name: string 
}

type B = Pick<A, 'name'>

//Output 1
type B = {
  name: string 
}

type B = Pick<A, 'id' | 'name'>

//Output 2
type B = {
  id: number 
  name: string
}

Omit

Omit<T, 'key1' | 'key2'>
Утилита предназначена для создания типа из оставшихся (не исключенных) свойств объекта.

type A = {
  id: number 
  name: string 
}

type B = Omit<A, 'id'>

//Output 1
type B = {
  name: string 
}

type B = Omit<A, 'id' | 'name'>

//Output 2
type B2 = {}

Exclude

Exclude<T, U>
Утилита создает union тип, исключая свойства, которые уже присутствуют в двух разных типах. Он исключает из T все поля, которые можно назначить U.

type A = {
  id: number
  name: string
  length: number
}

type B = {
  id: number
  color: string
  depth: string
}

type C = Exclude<keyof A, keyof B>

//Output 
type C = "name" | "length"

Extract

Extract<T, U>
Создает union тип, извлекая из T все члены объединения, которые можно назначить U.

type A = {
  id: number
  name: string
  length: number
}

type B = {
  id: number
  name: string
  color: string
  depth: string
}

type C = Extract<keyof A, keyof B>

//Output 
type C = "id" | "name"

ReturnType

ReturnType<T>
Создает тип, состоящий из типа, возвращаемого функцией T.

type A = () => string 

type B = ReturnType<A>

//Output
type B = string

Это одни из основных Utility Types, в материалах к статье я оставлю ссылку на документацию, где при желании вы сможете разобрать остальные утилиты для продвинутой работы с TS.

Conditional Types

В TypeScript есть возможность создавать типы в зависимости от передаваемого дженерика.

type ObjProps = {
  id: number 
  name: string 
}

type ExtendsObj<T> = T extends ObjProps ? ObjProps : T

const obj1: ObjProps = {
  id: 0, 
  name: 'zero'
}

const obj2 = {
  id: 1
}

type A = ExtendsObj<typeof obj1> // type A = ObjProps
type B = ExtendsObj<typeof obj2> // type B = { id: number }

Mapped Types

Сопоставленные типы позволяют вам взять существующую модель и преобразовать каждое из ее свойств в новый тип.

type MapToNumber<T> = {
  [P in keyof T]: number
}

const obj = {id: 0, depth: '1005'}

type A = MapToNumber<typeof obj>

//Output
type A = {
  id: number 
  depth: number 
}

Type Guards

Если тип не определен или неизвестен, то на помощь разработчику приходит "защита типов".

typeof

Самый простой способ обезопасить себя от ошибки, напрямую проверить тип при помощи оператора typeof (ранее в примерах вы могли видеть использование этого оператора, который возвращает тип переменной).

const fn = (val: number | string) => {
  if (typeof val === 'number') {
    return ...
  }

  throw new Error(`Тип ${typeof val} не может быть обработан`)
}

in

Еще один из способов защитить тип, использовать in, этот оператор проверяет присутствие свойства в объекте.

const obj = {
  id: 1,
  name: 'first'
}

const bool1 = 'name' in obj  //true
const bool2 = 'foo' in obj  //false

instanceof

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

function C() {}
function D() {}

const o = new C();

o instanceof C //true
o instanceof D //false

is

Этот оператор указывает TypeScript какой тип присвоить переменной, если функция возвращает true. В примере ниже оператор is сужает тип у переменной foo (string | number) до string. Это определенная пользователем защита типа. Благодаря защите компилятор приводит тип до определенного внутри блока if.

interface FirstName {
    firstName: string
}

interface FullName extends FirstName {
    lastName: string
}

const isFirstName = (obj: any): obj is FirstName => {
    return obj && typeof obj.firstName === "string"
}

const isFullName = (obj: any): obj is FullName => {
    return isFirstName(obj) && typeof (obj as any).lastName === "string";
}

const testFn = (objInfo: FirstName | FullName | number) => {
    if (isFullName(objInfo)) {
        console.log('Тип FullName')
    } else if (isFirstName(objInfo)) {
        console.log('Тип FirstName')
    } else {
      console.log('Тип не принадлежит FullName или FirstName')
    }
}

testFn({ firstName: 'Andrey' }) //Тип FirstName
testFn({ firstName: 'Andrey', lastName: 'Maslov' }) //Тип FullName
testFn(1) //Тип не принадлежит FullName или FirstName

Заключение

Как видите, TypeScript - это мощный инструмент для разработки, который позволяет улучшить качество вашего кода, сделать его более надежным и легко поддерживаемым. В этом туториале мы рассмотрели приемы работы с TypeScript, над такими продвинутыми темами, например, как дженерики и type guards.

Не забывайте, что изучение TypeScript - это постоянный процесс, и чем больше вы практикуетесь, тем более уверенно будете использовать его в своих проектах.

Если у вас возникнут вопросы или потребуется дополнительная помощь, обращайтесь к официальной документации TypeScript или дополнительным материалам.

Материалы для изучения

Основы
Utility Types
Шпаргалка по TS в картинках

Теги:
Хабы:
Всего голосов 20: ↑16 и ↓4+12
Комментарии12

Публикации

Истории

Работа

Ближайшие события

Конференция «Я.Железо»
Дата18 мая
Время14:00 – 23:59
Место
МоскваОнлайн
Антиконференция X5 Future Night
Дата30 мая
Время11:00 – 23:00
Место
Онлайн
Конференция «IT IS CONF 2024»
Дата20 июня
Время09:00 – 19:00
Место
Екатеринбург