В документации TypeScript машинописный текст определяется как строго типизированный язык программирования, основанный на JavaScript. TypeScript был создан и поддерживается Microsoft, а их слоган для языка - «Масштабируемый JavaScript». Как вы, вероятно, можете заключить из слогана, TypeScript построен на основе JavaScript, что позволяет нам работать с JavaScript, одновременно расширяя функциональные возможности, чтобы помочь сохранить наше приложение организованным и менее подверженным ошибкам.

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

Зачем использовать TypeScript?

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

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

Рассмотрим приведенное выше изображение ошибки, на котором мы передали в функцию неверный параметр. Просто прочитав ошибку, мы можем быстро сделать вывод, что тип нашего параметра должен быть строкой, а не числом. Более того, мы даже не сможем скомпилировать наш код, если не исправим проблему, потому что TypeScript выдаст ошибку при передаче неправильных значений. В проекте без TypeScript мы бы реализовали это только после запуска функции. Мы полностью избегаем проблемы. Довольно аккуратно!

Основные типы

Прежде чем мы начнем объявлять аннотации типов, мы должны отметить, что JavaScript поставляется с семью встроенными типами, примитивами JavaScript. Эти:

  • строка: последовательность символов
  • number: числа меньше bigint
  • bigint: числа больше 2⁵³ - 1
  • логическое: True / False
  • undefined: undefined
  • символ: Символ (‘*’)
  • ноль ноль

Мы можем вывести тип переменной, с которой мы работаем, оценив console.log (typeof variableName). Мы будем использовать эти примитивы вместе с другими типами для аннотаций наших типов.

Любые, недействительные и неизвестные типы

Any type
function errorLogger(complexRemoteObj: any) {
  ...
}
Void type 
const logError(errors: string []) :void => {
  for(let error of errors) {
    console.log(error)
  }
}
Unknown type
const emailError(error1: unknown, error2: unknown) {
  if(typeof error1 === 'server' && typeof error2 === 'data') {
    ...sending email 
  }
}
Try it out here

Прежде чем углубляться в объявления типов, давайте рассмотрим типы any, void и unknown.

Тип any - это то, как мы говорим TypeScript не запускать какие-либо проверки типа для значения, которое мы определяем. Первоначально он использовался, когда TypeScript не мог обрабатывать сложные объекты, но теперь его использование в основном не рекомендуется, поскольку TypeScript может обрабатывать самые сложные объекты.

Тип void используется для явного определения того, что функция ничего не возвращает. TypeScript может определять возвращаемые типы, поэтому в этом нет необходимости, если только мы не хотим подчеркнуть, что функция не имеет возвращаемого значения.

Неизвестный тип аналогичен любому типу с точки зрения разрешения любого значения, но когда мы используем неизвестный тип, проверки Typescript не отключаются. Мы можем проверить типы передаваемых значений для выполнения операций. Неизвестный тип помогает защитить наш код от типов.

Определение типов

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

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

Аннотации встроенного типа

Common Declarations
variable
const hometown: string = 'NYC';
array
const numberArray: Array<number> = [1,2,3];
  or  
const stringArray: string[] = ['a','b','c'];
tuple
const pet: [string, number] = ["Toby", 27];
object
const pet: {name: string; age: number;} = {
  name: 'Toby';
  age: 27;
}
Try it out here

Аннотации встроенных типов - это простой и удобный способ объявления типов. Для переменных используется следующий синтаксис: имя переменной, двоеточие, за которым следует тип переменной и определение значения («имя_переменной: тип_переменной = значение»). В приведенном выше определении переменной мы объявили переменную родного города строкового типа и установили ее значение NYC. Я также включил другие распространенные встроенные объявления, которые полезно знать при объявлении типов и интерфейсов.

Inline Function Declarations
Regular function
function greetNthTimes(greeting: string, xTimes: number) : string {
  let str = ''
  for(let i = 0 ; i <= xTimes; i ++) {
    str += `${greeting} \n`
  }
  return str
}
console.log(greetNthTimes('hi', 10))
Arrow function
const greetNthTimes = (greeting: string, xTimes: number) : string =>  {
let str = ''
for(let i = 0 ; i <= xTimes; i ++) {
    str += `${greeting} \n`
  }
  return str
}
console.log(greetNthTimes('hi', 10))
Try it out here

Мы можем назначать аннотации типов к функциям, включая стрелочные функции. После параметров указывается двоеточие и его тип. В нашей первой функции выше мы определяем функцию, которая принимает приветствие и печатает его в новой строке x раз. Их соответствующие параметры - строка и число.

Иногда мы создаем функции с необязательными параметрами. Чтобы сделать параметр необязательным, мы добавляем вопросительный знак (?) После имени параметра и перед объявлением типа. Имейте в виду, что при работе с аннотациями встроенного типа мы можем сделать только последний параметр необязательным. Если нам нужна большая гибкость, мы можем объявить интерфейсы или псевдонимы типов.

Псевдонимы типов

type Person = {
  age: number;
  name: string;
  email?: string;
}
Try it out here

Псевдоним типа - это псевдоним для объявленного типа, на который мы затем можем ссылаться в различных частях нашего приложения и в других псевдонимах или интерфейсах. Чтобы объявить псевдоним типа, мы ставим перед объявлением слово type, за которым следует желаемое имя псевдонима с заглавной буквы, и устанавливаем его равным объекту, который содержит аннотации наших типов.

В приведенном выше примере мы объявили псевдоним типа человека со свойством age типа number и свойством name типа string. Мы также объявили необязательный параметр электронной почты, потому что не у каждого человека есть электронная почта.

Интерфейсы типов

Interface Person {
  age: number;
  name: string;
  email?: string;
}
Try it out here

Интерфейс очень похож на псевдонимы типов. Мы объявляем интерфейс, добавляя к желаемому имени интерфейса значение «Интерфейс» и открывая объект, в котором мы объявляем аннотации наших типов. Как видно из приведенного выше примера, псевдонимы типов выглядят почти идентично интерфейсам; В большинстве случаев они взаимозаменяемы. В документах TypeScript отмечаются их различия, поскольку «ключевое отличие состоит в том, что тип нельзя повторно открыть для добавления новых свойств, а интерфейс всегда расширяемый».

Типы союза

type FavoriteChar: string | number | Symbol | null

Типы объединения - это типы, образованные путем объединения нескольких существующих типов. Мы объявляем тип объединения, разделяя существующие типы вертикальной чертой (|). Наш приведенный выше тип favavourChar может принимать любой из типов, которые мы включили в наш тип объединения. Если нам нужно более четко указать значения, которые мы хотим разрешить, мы можем использовать строковый литерал.

type Borough: "Queens" | "Brooklyn" | "Staten Island" | "Bronx" | "Manhattan" 

Типы строковых литералов позволяют сузить допустимые типы до указанных значений. Если бы у нас была функция, которая ожидает район Нью-Йорка, мы могли бы использовать приведенный выше тип объединения строкового литерала, чтобы гарантировать, что мы принимаем только 5 районов.

Типы пересечений

type Name {
  name: string; 
}
type Age {
 age: number
}
type Breed {
  breed: string 
}
type Pet = Name & Age & Breed
const toby: Pet {
  name: 'toby';
  age: 27;
  breed: 'lab';
  }
Try it out here

Типы пересечения похожи на типы объединения в том, что они позволяют нам сформировать новый тип, используя существующие типы, но пересечение будет сформировано из всех типов, используемых для создания пересечения. Мы определяем тип пересечения с помощью знака амперсанда (&) между типами, которые мы будем использовать. В нашем примере выше мы использовали три разных типа для создания нашего типа Pet.

Общие типы

Array Generic Type 
const stringArray: Array<string> = ['hi','hello','i']
const numberArray: Array<number> = [1,2,3]

Универсальные типы позволяют нам определять типы при определении, передавая типы как значения, разделенные запятыми, внутри угловых скобок. Один из универсальных типов, с которыми вы можете столкнуться чаще всего, - это универсальный тип массива, как показано выше.

Regular Generic function 
function logChar<ItemType1>(char: ItemType1, x: number) :void {
for(let i = 0 ; i <= x; i++) {
  console.log(char)
  }
}
console.log(logChar<string>('howdy!', 10))
Arrow Generic function
const logNum = <ItemType1,>(char: ItemType1, x: number) => {
  for (let i = 0; i <= x; i++) {
    console.log(char);
  };
}
Try it out here

Точно так же мы можем объявить общие функции, передав общие параметры в угловых скобках, а затем ссылаясь на них в наших параметрах функции. Мы даже можем объявлять универсальные стрелочные функции с одним предостережением: мы должны использовать запятую после объявления универсального параметра, потому что в противном случае компилятор будет думать, что мы работаем с элементом реакции.

Псевдонимы и интерфейсы универсального типа

interface Form<Type1> {
  value: Type1;
}
type Form<Type1> {
  value: Type1;
}

Объявление псевдонимов и интерфейсов универсального типа аналогично. Мы объявляем интерфейс или имя типа, за которым следуют угловые скобки с нашими параметрами-заполнителями, а затем ссылаемся на них в нашем типе или интерфейсе.

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