Transcript for:
Обзор TypeScript

рад вас приветствовать друзья в новом видео техническом видео после долгого перерыва и как вы могли догадаться это курс фундаментальный курс по Typeesриpt пару минут небольшая водна и будем начинать я очень долго откладывал запись этого курса именно по Typeescript потому что не видел в этом необходимости и мне всегда казалось что это будет просто пересказ документации но у меня на работе коллега проводил целую серию лекций потеприпt и даже я с опытом там на ТС 5-6 лет узнал для себя что-то новое я подумал что можно тоже попробовать записать ролик чтобы это был не просто пересказ документации а именно фундаментальные знания которые позволят тебе не просто интуитивно писать на тайпскрипте интуитивно писать не трудно в целом для 90% кейсов тебе этого хватит а чтобы ты именно фундаментально понимал как это всё работает и как тебе в сложных ситуациях понимать как Typeesрипt устроен и за счёт этого как бы бустануть свои знания при составлении сценария этого видео я пересмотрел наверное несколько десятков от других авторов видео по Тайпскрипту честно говоря одно и то же и вот я постарался сделать так чтобы мой контент отличался чтобы было именно больше вот этого фундамента основы не просто это стринг это массив это интерфейс это тип они отличаются этим это здесь естественно тоже будет какую-то базу я буду рассказывать но всё же акцент будет именно на подходе на том как правильно строить типы как к ним относиться как их воспринимать и задача сделать этот курс таким чтобы он был актуален на годы вперёд если ты смотришь это видео спустя 2-3 года после его выхода пожалуйста отпишись об этом в комментарии будет интересно почитать актуален он не актуален интересно полезно ну и на этом друзья сводной заканчиваем буду очень рад вашей поддержке в виде комментариев лайков после долгого перерыва и напоследок ещё напомню что у меня есть Telegram-канал там нет спама какой-то рекламы только по существу публикация роликов оповещения также я иногда там провожу опросы на какие темы снять ролики буду делиться какими-то интересными фактами инсайтами сейчас я возвращаюсь к активной YouTube деятельности и поэтому буду рад каждому кто присоединится ну а мы начинаем и ознакомимся с планом на этот урок здесь я предлагаю прямо детально не останавливаться если что поставьте на паузу и прочитайте кратко расскажу что мы будем рассматривать много разных концепций виды типизации чем они отличаются сужение типов концепция супертипов и подтипов как правильно воспринимать типы взаимодействие между ними разберём особенности специальных типов Never Unknown Any Void посмотрим как они вписываются в систему супертипов и подтипов в общем интересного будет много полезно будет как совсем начинающим кто с тейпскриптом знаком слабо так и тем кто уже в теме ну единственное добавлю что перед тем как смотреть этот ролик естественно JavaScript вы уже на каком-то уровне должны понимать с ведением на этом заканчиваем и приступаем к обучению итак Typeesрипt для начала необходимо начать с проблематики это наверное самое важное потому что часто начинающие не понимают зачем это нужно ведь они и так отлично справляются и пишут проекты на JavaScript от начинающих часто можно услышать такое заявление что JavaScript - это нетипизированный язык программирования и это неправильно он таковым не является javascript естественно это типизированный язык там есть система типов с которыми мы работаем но типизация там слабая и что важно динамическая теперь разберём эти два понятия что же такое слабая типизация слабая типизация позволяет нам работать с разными типами при этом неявно их преобразуя вот яркий пример манипуляции со строками и с числами javascript нам не запрещает так делать и при этом на выходе у нас может получиться разный результат где-то у нас конкатенация на выходе строка с умножением вот вообще не очевидно строку умножаем на строку получаем число это есть слабая типизация в массив мы можем добавить объекты числа строки и любые другие структуры с разными типами ну и в третьем примере вот ещё сравнение строгого и нестрогого равенства в первом случае нам сравнение строки и числа вернёт true во втором случае естественно false в общем слабая типизация позволяет нам без лишних манипуляций легко преобразовывать типы не запариваться об этом нужно просто знать особенности это и плюс и минус одновременно именно поэтому собственно говоря и появился Tpeesрипt теперь поговорим про динамическую типизацию она подразумевает что тип у нас определяется во время выполнения программы то есть в рантайме а не на этапе компиляции не на этапе написания кода как в строго типизированных языках где мы заранее при создании какой-то переменной объекта указываем её тип вот со скрина можно заметить что мы сначала объявили переменную с типом число потом легко её заменили на строку а потом вообще на объект в строго типизированных языках без каких-то явных преобразований ты так сделать не сможешь ещё один примерчик определение типа в рантайме у нас передаётся в функцию какая-то дата любая если эта дата есть мы возвращаем строку если этой даты нет возвращаем число ну то есть тип который возвращается в этой функции он вычисляется по условию динамически почему собственно говоря Typeesриpt исправляет всё это и почему то о чём мы сейчас говорили является проблемой когда мы пишем на строго типизированных языках как правило вот эти типы они являются такой самодокументацией это позволяет удобно быстро понять с какими вообще данными мы работаем в маленьких проектах это не обязательно ты всё можешь удержать в голове но в больших проектах это уже превращается в проблему да можно и на JS писать так что будет документация всё красиво но это усложняет плюс из-за отсутствия строгой типизации у тебя появляются проблемы с дебагом и тестированием кода когда работают несколько разработчиков ты ожидаешь что тебе там придут одни данные тебе приходят другие одного поля нет тут это поле появляется ты не понимаешь почему и тратишь кучу времени на отладку по идее Typeesриpt на 100% эту проблему не решает однако за счёт того что у тебя есть строгая схема описания типов ты уже примерно можешь догадаться что пошло не так ну и конечно же приятный бонус от строгой типизации - это то что у тебя в среде разработки появляется адекватный автокомплит он сразу тебе подсказывает какие поля и методы есть у объекта и как с ними работать итак с проблематикой определились надеюсь те кто недавно занимаются программированием поняли в чём проблема теперь поговорим про непосредственно Typeesриpt typeesриpt является надмножеством на JavaScript то есть по факту он содержит в себе всё что есть в JavaScript но расширяет его добавляет вот всё что связано с типизацией ну и можно сделать такой вывод что если ты знаешь JavaScript то ты уже наполовину знаешь Typeesриpt если мы сейчас посмотрим на те же примеры которые смотрели на JS но уже с участием компилятора ТС то ещё на этапе написания кода за счёт подсказок среды разработки мы уже увидим с какими типами мы работаем сверху 4 - это строка array у нас может содержать вот такие вот данные аварей был у нас изначально объявлен как число и просто так поместить строку мы туда уже не можем как я уже сказал хоть и в JS типизация слабая но она всё-таки есть и работаем мы с базовыми стандартными типами данных это примитивы их семь штук и объекты под объектами подразумеваем все ссылочные типы данными такими являются и функции и даты и массивы это по факту всё объекты ссылочные типы данных все кто смотрит это видео наверняка это знают но всё же лучше уточнить что примитивы у нас при вот таких вот манипуляциях при перекидывании из одной переменно в другую у нас копируется именно значение этих переменных и в памяти это всё хранится в стеке примерно вот в таком вот виде несмотря на то что в secondн мы поместили значение first оно просто скопировалось то есть никакой ссылки изначально на first там не существует и не хранится а вот с ссылочными данными это работает несколько наоборот когда мы объявляем объект массив или любой другой ссылочный тип данных в стеке хранится не само значение не сам объект а хранится адрес на тот кусок памяти в куче в котором уже вот значения этого объекта хранятся и когда мы из одной переменной присваиваем объект в другую переменную то там копируется именно вот этот адрес поэтому если у вас есть две переменные которые ссылаются на один адрес в памяти то при изменении одного объекта вы будете менять и второй объект это вообще никак с тайпскриптом связано не было просто минутка полезной информации итак возвращаемся к Typeesриpt по сути здесь мы работаем с такими же примитивами с такими же объектами из примитивов строим более сложные структуры то есть объекты но поверх всего этого добавляется строгая типизация если JavaScript со слабой динамической типизацией то Typeescript уже язык программирования со статической типизацией строгой при этом она может быть явной или выводимой и структурной терминология на первый взгляд непростая на самом деле и опытные разработчики часто не очень понимают что все эти слова значат они просто их запомнили но не понимают что это такое поэтому здесь важно прямо разобраться для лучшего понимания в дальнейшем итак идём по порядку что такое статическая типизация статическая в отличие от динамической подразумевает что типы проверяются на этапе компиляции и написания кода а не в рантайме если в JS вы объявили строку потом поместили в неё число а потом вообще какой-нибудь массив то в ТС так уже не получится если вы заранее это не описали дальше явная или выводимая типизация в примере слева мы видим что мы явно указываем что у нас переменная типа number во втором случае typeрипt по значению сам определит и сам этот тип выведет при этом в данном случае тип number зафиксируется и поместить туда же строку например вы уже не сможете со статической явной или выводимой типизацией разобрались что такое структурная типизация слева и справа у нас объявлены типы с разными названиями но с одинаковыми полями и там и там есть имя и там и там есть фамилия так вот с точки зрения Typeescript за счёт того что типизация структурная эти типы взаимозаменяемые то есть если какая-то функция на вход ожидает тип person а вы передадите туда тип usеer то Typeesрипту будет вообще всё равно он этот тип с радостью примет и всё будет хорошо многие языки программирования не имеют структурной типизации и там если вы передадте в тип person тип user у вас будет ошибка в Тайпскрипте же есть вот такая вот гибкость ну и при написании кода это всегда стоит учитывать это не какая-то магия это просто особенности структурной типизации которая в Typeesриpt заложена мы уже обсудили что в JavaScript у нас семь примитивных типов и ссылочные типы данных объекты грубо говоря в Typeesриpt в принципе ситуация похожая из примитивов строятся более сложные типы но также в Typeesриpt есть ещё специальные типы их четыре ну можно сказать пять если сюда добавлять ещё литералы обо всех мы очень подробно поговорим пока на этом останавливаться не будем просто запомним что есть вот такие вот типы также все типы в Typeescript можно поделить на такие своего рода группы с которыми мы обычно работаем это примитивы и специальные типы о которых мы поговорили составные типы - это объекты массивы то есть более сложные структуры также у нас есть литералы и Union Intersection типы сейчас может быть для многих непонятно но не переживайте всё подробно разберём как я сказал в самом начале многие люди знают Typeesрипt интуитивно и не понимают как он на самом деле устроен что из себя представляют типы и шаг лево шаг вправо сразу всё ломается и люди уже не знают как с ним работать так вот чтобы правильно относиться к типам нужно воспринимать их как множество вот у нас есть множество строк есть множество чисел множество булин значений причём что стринги и намры - это у нас бесконечные множество а булян ограниченный там всего два значения: true либо false при этом важно понимать что ну string number бун - это примитивные множества но у нас могут быть множество и сложные которые вот как раз объекты составные какие-то типы и там с множествами уже могут идти различные операции одно множество может быть под множеством или над множеством другого при этом над множествами можно также совершать операции ну два классических это объединение и соответственно пересечение давайте поговорим сначала про объединение так называемый union тип это словосочетание вы будете слышать постоянно когда будете работать с Tapeescript и он как раз подразумевает объединение множеств смотрим на первый скрин там дата у нас является объединением number или string это означает что такой переменной мы можем присвоить как строку так и число то есть в данном случае вот это объединение можно воспринимать как или то есть либо строка либо число опять же в голове представляем два множества мы их объединяем получаем третье множество которое может быть либо первым либо вторым это что касается множеств с примитивами что если у нас будут объекты например есть main инфо и какое-то дополнительное инфо которое содержит ещё и возраст при объединении двух этих множеств мы получаем следующую картину у нас может возникнуть три ситуации три объекта которые мы в итоге можем получить первый - это когда у нас содержатся в итоговом типе поля объекта X первого объекта первого типа второе - это когда содержатся поля типа Y второго типа второго объекта ну и третье соответственно это их пересечение когда объект содержит и поля типа X и поля типа Y все примеры с кодом мы будем рассматривать в плейграунде Typeesрипта здесь мы будем писать вот такие вот примерчики здесь у нас будет самая актуальная версия тейпскрипта слева мы пишем код справа видим результат компиляции если будут какие-то ошибки соответственно нам здесь всё будет подсказывать здесь прямо сразу можно настроить тестконфиг пока об этом не думаем о нём будем говорить чуть позже так с объединением здесь в целом всё понятно давайте поговорим про пересечение здесь наверное всё ещё проще обозначается оно знаком амперсанта вот так вот ну и пересечение подразумевает что итоговый тип включает у нас только то что есть в типе X и то что есть в типе Y то есть если объединение мы воспринимаем как такое логическое или своего рода то пересечение - это как логическое и и вот здесь вот сразу 5 секунд на подумать что будет в результате вот такого вот пересечения раз 2 3 4 5 со специальным типом Never мы ещё не знакомы однако здесь его можно воспринимать как некое недопустимое множество то есть пересечение между числами и строками не существует объединение может быть то есть либо строка либо число а вот пересечение когда у нас и строка и число одновременно такого естественно быть не может и Typeescriptрипt вам об этом сообщит ну если рассматривать пример уже с объектами вот таким вот образом объявляем пересечение ну и здесь понятно что итоговый результат у нас будет включать и поля первого и поля второго объекта запускаем Playground union меняем на intersection ну и сразу видим что Info1 и info 2 у нас с ошибкой то есть здесь у нас отсутствует Property Ag во втором случае у нас отсутствует вот видно last name и first name из типа main info эти два инструмента очень часто используются Typeesриpt они делают его более выразительным гибким можно комбинировать объединять делать пересечения любых объектов и гибко с ними работать ну и небольшое резюме относимся к типам как к множествам на самом деле вот это вот простое понимание оно чуть-чуть расширяет ваше восприятие тейпскрипта и многие проблемы с которыми вы будете сталкиваться они решаются гораздо проще думаю с этим понятно двигаемся дальше и переходим также к важнейшей концепции которую необходимо освоить для лучшего понимания TypeesриP в будущем это концепция супертипов и подтипов итак здесь концепция в целом простая смотрим на скриншот видим что у нас есть один тип который называется SuperType и второй Subtype видим что subtype содержит все те же поля которые есть у SuperType но добавляет ещё свои то есть если проводить какую-то аналогию с ООП супертип - это такой базовый класс базовый тип от которого ну тут конечно какого-то явного наследования нет но можно сказать что вот эти поля они грубо говоря наследуются то есть саб-типом считается тот тип у которого есть поля родителя но при этом он добавляет ещё свои при этом как я уже сказал какого-то явного наследования может не быть потому что типизация структурная и Typeesриpt видит что эти поля совпадают и смотрим вот теперь на правый скриншот что мы здесь видим мы объявляем саптип объявляем супертип и как видите при попытке присвоить супертипу сабтип у нас всё хорошо то есть тип у которого полей больше чем в другом типе может спокойно вот так вот присваиваться то есть если прямо совсем-совсем упростить когда мы идём от большего к меньшему а такой вариант допустим как бы лишние вот эти поля они откидываются и остаётся вот только сухой остаток который есть в супертипе и как вы уже могли догадаться обратное уже будет невозможно без каких-то явных преобразований типов то есть здесь что мы видим здесь ситуация обратная мы сначала создаём супертип и пытаемся присвоить его сабтипу подтипу и видим что Tapeesриpt такое не принимает то есть в данном случае у нас будет ошибка о том что не хватает полей в данном случае поле H у нас обязательное в супертипе его нет и соответственно такое присвоение недопустимо в таком простом примере естественно это кажется очевидным но в реальной практике это не всегда настолько прозрачно и важно понимать почему это происходит давайте немного резюмируем подтип обязательно должен включать все поля и методы которые есть у надтипа и при этом он может добавлять свои второе вытекает из первого что надтип содержит меньше свойств и методов чем подтип ну и третье - это как раз вот эти вот примеры которые мы разбирали что подтип может быть присвоен переменной надтипа а обратное без явных преобразований типов обычно невозможно здесь ещё уточню потому что многие воспринимают слова буквально здесь речь идёт не только про присвоение можно передавать э вот таким же образом что-то в функцию куда-то там я не знаю в класс в конструктор что-то с этими данными делать то есть речь идёт не только про присвоение сейчас эту концепцию важно понять потому что когда мы будем изучать специальные типы это очень-очень пригодится итак с этим разобрались двигаемся дальше теперь пройдёмся по основным группам типов о которых я уже говорил ну и начнём с самого базового с примитивов здесь всё в принципе так же как в обычном JavaScript семь примитивов: string number begin bullan undefined null и symbol ну и давайте смотреть самая-самая база как я уже говорил тип мы можем указывать явно вот таким вот образом через двоиточие либо же Typeesриpt может его выводить самостоятельно как видите если мы в numberр передаём строку у нас сразу же Typeescriриpt начинает ругаться это как раз и предотвращает большинство ошибок связанных с типами ещё на этапе разработки ну и таким образом мы можем указывать любые типы опять же повторюсь что делать это специально вот так вот указывая не обязательно потому что Typeesриpt сам может вывести этот тип так ну с примитивами как в других роликах полчаса мы солить не будем давайте перейдём к более интересным темам и поговорим о специальных типах вот тут уже есть о чём поговорить не всё так очевидно итак в ТС четыре это any unnown never и void у каждого из них естественно своя функция свой смысл и все они очень важны и часто используются но в то же время с каждым из них ну кроме Void нужно быть аккуратным сейчас по очереди по каждому пройдёмся посмотрим как они вписываются в систему супертипов и подтипов как с ними работать чего остерегаться какие есть подводные камни так начнём с первого и самого простого для понимания это Эни для начинающих Эни - это друг для проектов Эни - это враг итак Эни можно сказать отключает полностью любую проверку типов когда вы указываете вы можете использовать всё что угодно: строки массивы числа функции и Typeesриpt никак это проверять не будет ну и очевидно что это убивает любую эффективность Typpeesриpt почему я сказал что для начинающих это друг часто начинающие сталкиваются с ситуациями когда они не понимают как типизировать не понимают как решить какую-то проблему ошибку с которой они столкнулись и вот на этапе обучения чтобы сильно не буксовать на одном месте иногда это использовать валидно но важно понимать что в реальном проекте использовать нельзя потому что это всю ну всю эффективность весь смысл тейпскрипта убивает когда вы объявляете функцию если явно не указать тип аргументов они по умолчанию будут когда вы настраиваете Typeescript здесь есть вот такая вот опция про TS config мы будем подробно говорить но в общем эта опция она запрещает вот такое вот неявное как бы то есть нам обязательно нужно указывать тип аргумента при этом если вы руками прямо укажете что это то всё будет хорошо и вот обратите внимание если эту опцию мы выключим как бы ошибка пропадает как бы рекомендация врning остаётся но ошибки уже нет но эту опцию лучше по-хорошему всегда включать потому что типы лучше всегда вот в таких случаях указывать явно если вам на этапе обучения нужен any то лучше указать его явно чем он вот выведется самостоятельно тайпскриптом итак давайте немного резюмируем как я уже сказал Эни отключает вообще любую проверку типов и ну вот всю эффективность тепскрипта убивает на этапе обучения на этапе написания петпроектов чтобы не буксовать долго с какими-то сложностями использовать ОК использовать его в продакшн проекте в некоторых случаях можно чуть позже мы об этом поговорим но в 99,9% случаев не стоит теперь поговорим про то как ни вписывается вот в эту концепцию множеств системы подтипов и надтипов по факту any просто существует одновременно везде ни надтип и подтип одновременно для всего то есть ты можешь присвоить как any любому какому-то значению так и этому значению которое объявлено как эни любое другое и каких-либо проблем не возникнет потому что ну по факту все проверки отключены сейчас это важно потому что у unn тоже есть своё место в этой системе и потом мы всё это сравним и посмотрим как с этим работать сни думаю здесь всё понятно здесь всё просто давайте перейдём теперь кнон вот здесь уже всё немного поинтереснее укажем что у нас функция на вход ожидает если мы попробуем обратиться к дата через точка то естественно никаких полей нам Typeesриpt не подскажет кстати если с Эни мы сделаем то же самое то ну здесь в принципе ситуация будет похожа подсказок никаких не будет ну здесь понятно потому что непонятно что подсказывать теперь смотрите давайте попробуем создать какую-нибудь переменную вот эти val удалим чтобы у нас пересечений не было и попробуем указать что например это строка если мы попытаемся присвоить дата мы увидим ошибку что невозможно присвоить типу string но при этом если мы сделаем any естественно здесь всё будет хорошо если сейчас сказать кратко тоnown - это такой безопасный аналог any и смотрите в чём здесь история если мы сделаем проверку type of data равняется string то потом без проблем мы вот этому value сможем присвоить нашу дату и никаких ошибок не будет то есть unknown вынуждает нас делать проверки на типы и использовать их безопасно то есть если мы действительно не знаем с каким типом нам предстоит работать то нам необходимо использовать unnown это вынуждает нас делать проверки и работать с типами правильно вот здесь если навести на дата видимо если мы наведём на дата внутри проверки то здесь мы уже видим что это у нас массив ну опять же мы сделали проверку на то что это массив а массив с какими данными Tpeesриpt ну естественно не понимает мы это нигде не указали о том как правильно делать вот эти проверки на типы мы поговорим ещё чуть позже в соответствующем разделе но сейчас просто резюмируем что unknown - это такой безопасный способ когда мы не знаем какой тип нам ожидается на вход сделать его неизвестным и потом за счёт соответствующих проверок безопасно его обработать теперь опять же поговорим про то как это всё вписывается в концепцию супертипов и подтипов если был и супертипом и подтипом одновременно для всего тоnown является супертипом для всех типов но при этом он не может быть подтипом смотрим например переменная объявленная как unnown без проблем мы потом в неё можем и строку присвоить и число и булин и объект тут никаких сложностей нет но если мы повернём ситуацию наоборот и попытаемся присвоить какой-нибудь строке числу или любому другому типу то естественно такое сделать не получится и это опять же вот тут важно это всё осознать понять если вы это один раз поймёте потом вы это будете использовать вот просто на раз-два на ура пойдёт это всё исходит из того что unnown является супертипом и при этом не является подтипом ни для кого кроме себя ну и соответственно потому что ни для всего является подтипом и супертипом вот такую вот вставочку здесь сделаю напомнить на поставьте на паузу ещё раз вникните в это вернитесь на 15 секунд назад и посмотрите что там происходит с присвоением ну небольшая сравнительная табличка потом ещё с такую же нарисуем и вся картина станет ясной последние итоги any у нас и супертип и подтип для всех типов аноnown у нас супертип для всех но при этом не является подтипом никого кроме себя и ну и остаётся разобрать Never здесь тоже ситуация достаточно интересная я думаю вы уже догадываетесь как Never ведёт себя вот в этой системе супертипов и подтипов ну здесь чисто логически догадаться до этого не трудно ну если вы сказали о том что never является подтипом для всего но не является супертипом то вы оказались правые то есть здесь такая зеркальная ситуация с unnowм never - это такое своего рода пустое множество недостижимое значение то что не может существовать такой парадокс в типах если какая-то функция или какое-то выражение не может вернуть значение или не завершается нормально например там всегда пробрасывается ошибка то такая функция будет возвращать как раз never то есть эта функция она ничего не вернёт никогда потому что там всегда происходит ошибка или если функция содержит например бесконечный цикл она тоже никогда не завершится и тоже это по сути never и вот за счёт того что это подтип для всех других типов но при этом он не является супертипом считается что never - это самый узкий тип ну давайте посмотрим например с присвоением вот такой вот вариант вполне норм когда у нас объявлено value never мы его можем присвоить там строке числу то что мы не могли сделать с unknown never можно присвоить куда угодно а вот обратно уже то что мы могли как раз наоборот сделать здесь уже невозможно присвоить в never какое-то число значение строку невозможно ну и рисуем финальную заключительную сравнительную табличку any и unnown у нас уже здесь есть и добавляем сюда ещё и never ну да как я здесь уже говорил здесь ситуация полностью зеркальная unknown ну и по сути вот эти все три типа они закрывают вот эти вот базовые потребности с кейсами в которых нам необходимо обработать какие-то специфичные ситуации с супертипами подтипами ну и конечно же давайте сразу глянем примерчик где never может быть полезен сейчас мы создадим enam enam - это перечисление набор каких-то значений пока что просто относимся к этому так более подробно мы про это чуть позже поговорим и мы хотим сделать функцию которая принимает одно из этих значений это может быть либо first либо second давайте сразу эту функцию вызовем ну и передадим в неё вот допустим first ну и дальше такая классическая задача когда мы по какому-то условию хотим все эти значения обработать в случае если у нас одно значение сделать одно действие или вернусь какой-то один результат в случае другого действия сделать другое и вот сейчас мы по сути обработали оба возможных сценария и если мы зайдём в дефолтный кейс который должен отработать в конце да здесь у нас Tapeescript ругается что не все ветки возвращают значением давайте здесь сделаем return value и если мы посмотрим каким типом обладает value внутри вот этой дефолт ветки то мы увидим never почему это возникает потому что у нас есть всего два кейса: first и second мы оба обработали значит в дефолтном кейсе быть какого-то другого значения у нас уже просто не может то есть туда по сути попадает пустое множество недопустимое значение здесь value как видите равняется first здесь second ну а здесь never при этом если мы сейчас добавим третье значение то увидим что в дефолт ветку будет попадать именно оно и теперь самый главный вопрос: как здесь использовать never чтобы это было полезно и как раз здесь мы можем воспользоваться вот этой фишкой что мы понимаем как работают супертипы и подтипы мы можем создать переменную exosive check так называемая проверка объявить её как never и передать присвоить ей значение value сейчас никаких ошибок нет потому что value у нас never но как только мы добавляем третье значение мы видим что третье значение не может быть присвоено never то есть за счёт вот такого использования never понимания того как он работает мы можем сделать проверку функции которая гарантирует что каждый кейс мы обработали если кто-то из разработчиков э добавит новое значение у нас Tapeesриpt все места в которых это значение необходимо поддержать таким образом подсветит и мы не допустим какую-то ошибку в продакшене ну и ещё один примерчик мы его уже частично обсуждали когда говорили про Union и Intersection давайте создадим какой-нибудь тип и сделаем пересечение строки и допустим числа как мы уже обсуждали такое пересечение не может существовать поэтому тип этой переменной у нас получается never то есть Typeesриpt его сам выводит потому что понимает что эти множества не пересекаются и обратите внимание что never вот таким вот образом мы присвоить не можем потому что never - это тип присвоить мы можем только какое-то значение тип присвоить мы не можем так с этим разобрались ещё раз смотрим на эту табличку всё запоминаем обдумываем хорошенько и двигаемся дальше ну из специальных типов у нас остаётся ещё void здесь совсем всё просто void - это тип который означает что функция ничего не возвращает он используется активно и в других языках программирования ну вот в данном случае у нас есть функция fn которая просто вводит что-то в логе если мы наведём то мы увидим что она уже в принципе возвращает void по умолчанию то есть она ничего не возвращает при этом если мы явно укажем return und defined то ситуация здесь изменится но здесь важно помнить особенность джаваскрипта что даже если функция ничего не возвращает она по умолчанию всё равно возвращает undefined чаще всего это используется когда мы создаём тип для функции для какого-то колбка экшена действия функции обычно указывается вот такая стрелочная функция если она ничего не возвращается вот здесь указывается воid вот это суперраспространённая запись здесь мы можем указать как раз и аргументы с какими типами они будут то есть здесь мы именно создаём тип для функции такого я ещё не показывал здесь можно указать какое-то другое возвращаемое значение: массив число строку любой другой тип но часто вот используется именно void потому что функция ничего не возвращает со специальными типами разобрались то что мы изучили важно понять запомнить усвоить вникнуть и потом у вас всё просто как по маслу будет идти ну а мы переходим к составным типам основа основ то чем вы будете пользоваться на протяжении всей работы с TPScript самая важная его часть описание типов именно объектов для того чтобы начать описывать тип для объектов существует два способа: с помощью интерфейса и с помощью ключевого слова type в принципе на данном этапе можно считать что разницы между ними никакой нет чуть позже я расскажу отличия прямо наглядно рассмотрим но в базовом таком сценарии описания объекта можно считать что они равнозначны первый объявляется вот таким вот способом далее мы указываем поля которые этот объект должен содержать здесь вложенность может быть сколь угодно большой внутри объекта могут быть другие объекты можем даже сразу посмотреть первый интерфейс назовём как адрес укажем здесь поле сити например поле с улицей и поле с координатами это будет массив строк наверное даже вот так вот чиселкой укажем чтобы хоть какое-то отличие было ну и затем мы можем вот так вот указать что у нас в объект usе вложен другой объект адрес с вот таким вот типом ну и давайте сразу попробуем создать объект с таким типом через двоеточие указываем usеer ну и нам автокомпт сразу подсказывает какие поля у нас есть если мы заглянем внутрь адреса он также покажет какие поля есть и внутри адреса тест сразу же ругается что мы заполнили не все поля есть ещё два обязательных поля это C и Street если мы хотим сделать эти поля необязательными допустим нам координат достаточно чтобы определить адрес мы можем добавить вот такие вот вопросительные знаки и это сообщит Тайпскрипту о том что поля не обязательные так можно делать абсолютно для любых полей ну и как видите сейчас объект у нас стал полностью валидным ошибки ушли если мы вот эту опциональность необязательность убираем Typeescript будет требовать чтобы мы эти поля обязательно указали так ну вот сейчас верхнеуровневый объект мы заполнили единственное вот он у адреса сейчас требует C и Street сделаем их опциональными ну и всё объект полностью для ТСА валиден если мы хотим объявить массив таких юзеров мы просто добавляем к типу квадратные скобки внутрь массива добавляем объект и нам также автокомпт подсказывает какие поля нам необходимо заполнить ну соответственно с этим типом мы можем делать что угодно: принимать его аргументом функцию возвращать из функции с помощью таких составных типов мы описываем всё что угодно если вы пишете на реакте и пытаетесь создать компоненты хотите указать какие пробсы ожидаются на вкод вы описываете эти пробсы в виде интерфейса или типа здесь я уже сказал отличий каких-то нет и указываете все поля которые ожидаются на вход в этом компоненте если вы хотите типизировать ответ бэкэнда который вам приходит вы также создаёте тип response причём это тип может быть какой-то общий здесь будет какой-нибудь статус ошибка или успех ну и непосредственно сами данные которые из БКА приходят опять же здесь типизация может быть абсолютно любой какая требуется для вашей задачи причём мы точно не знаем какие данные намбка придут поэтому здесь как будто бы кажется что мы должны указать unknown но на самом деле есть другой механизм который называется generки обобщение но о них мы поговорим чуть позже просто пока что запомните что такое есть а обсудим детально с примерами мы уже попозже и также мы можем описывать и функции чуть раньше мы уже это делали но делается это вот таким вот образом здесь уже лучше использовать не интерфейс а тип через интерфейс тоже можно объявить функцию но синтаксис там не самый очевидный поэтому лучше использовать тип здесь всё прозрачно указываем стрелочную функцию что она возвращает и какие аргументы принимает типизируем всё стандартным вот таким вот базовым образом на базовом уровне здесь информации тоже достаточно чуть позже к каким-то деталям мы уже будем возвращаться и рассматривать их в отдельных темах union intersection мы уже непосредственно обсудили теперь давайте поговорим про литералы литералов существует несколько видов это строковые числовые булян литералы шаблонные строковые литералы у них там свои фишечки есть и составные литералы давайте посмотрим как с этим работать и что они из себя представляют итак что же такое литералы по сути литералы - это какие-то конкретные значения которые мы используем как тип делается это обычно для того чтобы сузить значение ну вот допустим мы хотим объявить цвета если мы просто укажем стринг мы можем записать туда абсолютно всё что угодно а если мы хотим уметь задавать только определённый набор строк Red Greгen Blue например в таком случае на помощь как раз приходят литералы указываются они вот таким вот образом через Union добавляем три цвета Red Green Blue и нам сразу Typeescript completтом подсказывает какие значения мы можем указать если мы попробуем передать что-то отличное от этих значений Typeescript сразу начнёт ругаться это может быть как несколько значений которые через Union вот так объединяются так и одно значение какое-то конкретное просто red просто Greгen или любое другое значение которое требуется для вашей задачи такие литералы можно использовать абсолютно для всего причём они могут быть не только строковые но и числовые например мы хотим задать какие-то размеры могут быть смешанными то есть мы можем комбинировать строки и числа также они могут быть булиан до этого на слайде были эти все виды описаны ну здесь по сути бульон - это всего два значения то есть используя такой литерал мы ничего от этого не выигрываем но мы можем использовать какое-то конкретное значение false или true вот это уже бывает в некоторых ситуациях полезно ну и получившийся тип с литералами мы соответственно можем использовать как хотим передать его аргументом функцию и что самое удобное мы сужаем круг вот этих вот возможных значений и вот кстати обратите внимание что color константа у нас не объявлено что она типа color но при этом функция спокойно это значение принимает ну как раз за счёт особенностей структурной типизации Typeesриpt сам понимает что это значение за счёт того что у нас объявлена константа подходит для этой функции но вот здесь уже есть нюансик если мы попробуем передать поле объекта вот таким вот образом Typeesриpt его не скушает поставьте на паузу и подумайте почему 1 2 3 Всё дело в том что поля объекта мы можем менять из-за того что это поле изменяемое Tapeesриpt воспринимает его какстрин а не как грин вот видите мы сейчас заменили спокойно и у нас там уже совсем другое значение чтобы сделать вот эти поля чтобы мы не могли их менять и Typeesриpt зафиксировал значение которое мы объявили можно воспользоваться вот такой конструкцией написать SC const и тогда как видите ошибка исчезает поля у нас становятся менять их нельзя и тогда Tpeesриpt опять поймёт что это значение оно соответствует как раз литералу давайте попробуем поменять color пытаемся заменить видим ошибку и видим что это поле read property поменять значение внутри мы просто-напросто не можем тоже достаточно удобно часто используется запоминаем и используем раз уж мы заговорили про IDE давайте ещё глянем на примере как его прямо в самом интерфейсе или типе объявить делается это вот таким вот образом тоже ничего хитрого нет если мы хотим чтобы поле было неизменяемое например айдишник который должен быть зафиксирован то мы объявляем его какonли так строковые и числовые литералы мы рассмотрели давайте посмотрим теперь с шаблонными строками как это работает тоже иногда бывает очень удобно если про это помнить создадим тип назовём его event name здесь таким же способом объявим несколько ретералов клик ну давайте какой-нибудь change ну и так далее там их может быть сколь угодно много input и другие теперь представим что у нас есть слушатели событий on click on change и мы хотим сделать другой тип который использует ивенты из первого типа и вот здесь мы используем как раз косые кавычки шаблонные литералы и вот таким вот образом мы можем создать on и затем передать event как видите у нас появились вот такие вот литералы onclick on change такие шаблонные строки можно делать сколь угодно длинными и использовать их как хотите давайте ещё какой-нибудь пример глянем например мы хотим сделать user ID и хотим чтобы строка с айдишником юзера начиналась ну допустим с usеer ID и затем чтобы шла уже какая-то уникальная чиселка и вот таким вот образом мы можем это сделать можем указать beginint можем указать string можем указать number и потом так как нам необходимо это использовать то есть этот тип будет соответствовать заданному только если перед началом самого числа используется user ID итак все пять типов мы рассмотрели: строковые числовые булиан шаблонные строковые литералы и составные литералы - это когда смесь например строка число строка булиан ну и мы двигаемся дальше и переходим наверное к самому сложному для восприятия у начинающих это дженерики или же обобщение сразу начнём с наглядного примера представим что у нас есть коробка она обладает какими-то характеристиками это длина ширина высота и в эту коробку можно поместить определённое содержимое как с точки зрения типов определить что у нас там строка число объект или может быть аnown указать потому что мы не знаем что там будет внутри или может быть и вот эту задачу как раз решают дженерики мы создаём условно Generic можем считать что это как аргумент для функции только аргумент для типа и потом когда мы используем тип класс функцию в которой мы задали вот этот Generic мы можем указать какой тип там будет использоваться в данном случае у нас заказ строка и число дженерики могут использоваться в интерфейсах типах функциях классах этот инструмент добавляет очень много гибкости в работу с кодом очень много инструментов с которыми вы будете работать работают поверх дженериков и это позволяет создавать реально универсальные функции универсальные классы в которых вы можете определять с какими типами вы будете работать лучше сразу рассмотреть на примере давайте попробуем создать интерфейс который будет описывать то что возвращает нам экend по IP что-то похожее мы 15 минут назад уже рассматривали добавим сюда какие-нибудь базовые поля по типу статуса которые у нас всегда статичные добавим сюда ещё какое-нибудь поле допустим с какими-нибудь метаданными ID запроса чтобы мы его потом в логах если что могли найти если что-то пошло не так ну и непосредственно сами данные а вот данные у каждого эндпоинта у каждого ответа свои и вот что за тип указывает для вот этих данных непонятно вдруг придёт строка или какой-то объект и вот собственно как я уже говорил дженерики эту задачу решают в одном случае нам приходят данные о пользователе в другом случае нам приходят данные о заказе в третьем ещё что-нибудь давайте сейчас каких-нибудь парочку базовых интерфейсов с которыми мы работать будем создадим пусть это будет usеer и допустим статья article добавим здесь какой-нибудь usернеam ну и здесь какой-нибудь заголовок статьи этого нам будет более чем достаточно как работать с дженериками как работать с обобщениями указываются треугольные скобки и внутри указывается название дженерика название дженерика может быть любым их может быть даже несколько но самый такой дефолт как обычно называют один аргумент один generнери - это Т сокращённо от type это грубо говоря такая стандартная конвенция но ничего не мешает вам назвать как-то по-другому ещё раз повторюсь Generic - это как аргумент функции только тип то есть это какой-то динамический тип который может быть у нас доступен внутри интерфейса класса или функции мы можем определить что у нас дата будет типа Т либо какое-то поле внутри этой даты с типом Т и Typeescriptрипt как видите сразу начинает вот этот type аргумент запрашивать он является обязательным если его указывать таким образом и точно так же как мы передаём аргумент функцию только в треугольных скобках мы передаём тип в данном случае мы указываем что в качестве поля дата у нас будет объект с типом user нам Typeescript сразу начинает это всё подсвечивать то есть он прокидывается внутрь этой даты и говорит нам: "Заполни поле usernр" ну что-то заполняем и видим что Typeesриpt ругаться перестаёт то есть AP respons мы уже сделали универсальным и в зависимости от переданного аргумента от переданного дженерика у нас тип будет работать по-разному то есть мы можем создать уже два разных объекта один from user app второй from article app как видите сейчас мы поменяли generic начинает ругаться TapeScript и говорит нам: "Замени поле на title это у нас строка ошибка пропала" то есть мы вот в тот самый ящик в ту самую коробку помещаем некий аргумент некий тип с которым мы в дальнейшем будем работать и как я уже сказал название Genericка может быть любое это не обязательно просто Т здесь мы можем писать и какие-то осознанные названия просто т это вот такоя общепринятая конвенция при этом этих типов может быть несколько то есть через запятую как аргументы функции вы их можете вот таким вот образом перечислять один для даты второй для метаданных третий для какого-нибудь value ещё для чего-нибудь и для соответствующих полей вы уже соответствующие типы указываете то есть ещё раз если подытожить за счёт дженериков мы делаем типы динамическими добавляем некоторые изменяемые типы внутрь которые доступны только в пределах вот этого скоупа интерфейса типа функции класса причём вы можете Generic даже вот так вот inлайн типом передать просто указать что мы ожидаем например какой-нибудь таймстamp там в метадаataта ну и в качестве value давайте его уберём вообще смысла от него сейчас мало если тип используется один раз можно его вот так таймстемпом передать если он у вас используется в нескольких местах можно его вынести отдельно чтобы переиспользовать здесь уже вопрос удобства исключительно ну вот так вот здесь используем мета вот и как видите сразу Typeescript подхватил её и сразу Автоcomete нам говорит что необходимо заполнить stamp с этим думаю должно быть понятно давайте ещё какие-нибудь примерчики рассмотрим например мы хотим написать универсальный тип для создания деревьев для описания деревьев у каждого элемента дерева есть свой айдишник есть какое-то название например и конечно же дочерние объекты то есть дерево оно имеет иерархическую структуру и получается что у нас тип ссылается сам на себя каждый элемент может иметь несколько потомков обязательно здесь надо сделать его необязательным или добавить нал для того чтобы вот эта рекурсия у нас была конечная потому что на самом нижнем уровне дерева на листьях получается у них уже никаких потомков не будет и нам не придётся указывать массив а просто там передадим нал ну и вот допустим у нас есть ещё здесь какое-нибудь value которое тоже может быть универсальным одно дерево у нас может на строках быть другое на числах третье на каких-то объектах дерево пользователей например храним какую-то иерархию семейное древо так сказать ну и непосредственно вот таким образом мы потом с этим деревом работаем вот здесь у нас в качестве value будет использоваться user укажем айдишник указываем value ну и сразу видим что здесь вот он подхватил поле usernр просит его заполнить ну попробуем добавить ещё какие-нибуд дочерние элементы ну также видим что вот он прекрасно всё подхватывает childron можно указать на так с этим я думаю понятно в принципе с интерфейсами и с типами всё достаточно легко всегда представляем вот эту вот коробку в которую мы динамически можем что-то поместить как работать с дженериками в функциях здесь в принципе всё аналогично у нас Generic может использоваться в аргументе в возвращаемом значении указывается вот так вот после названия функции в треугольных скобочках тоже и можно сделать например вот такой вот динамический genнериic в одном случае он будет там numberром в третьем стринг в четвёртом там объект в стрелочных функциях синтаксис чуть другой указывается перед скобками вот так generic но здесь важный момент когда вы используете JSX React он может пытаться воспринимать это как какой-то тегсовский вот и здесь необходимо добавить вот запятую тогда он будет понимать что это Generic ну и также этот Generic можно использовать как тип возвращаемого значения внутри функции если мы сейчас сделаем return arc ну то есть он ожидает у нас что у нас вернётся вот этот tвращаем ну при этом теперь мы можем при вызове функции любой тип указывать то есть вот сейчас он будет ожидать что мы передадим юзера на вход давайте даже убедимся в том что он этот тип подхватит сделаем чтобы значение возвращалось в какую-нибудь константу но если мы посмотрим да вот он подхватил он видит что с функции возвращается тоже usеer при этом здесь также можно использовать несколько дженериков один для например для аргумента второй для возвращаемого значения третий для какого-нибудь ещё аргумента то есть здесь тоже всё ограничено лишь задачей которую вы решаете если вы хотите объявить тип а не интерфейс то ну тут Generic в принципе точно так же указывается здесь никаких проблем с этим нет также в дженериках есть так называемые ограничения constraint мы можем заранее ограничить типы которые мы в дженерике будем использовать например сделаем функцию которая создаёт какую-то сущность и мы считаем что у любой сущности в нашем приложении должен быть обязательно например айдишник уникальный какой-то идентификатор который эту сущность отличает я не знаю там например дата создания или ещё какие-то поля и как видите у нас изначально уже TPSриpt подхватывает что у этого Gнерика есть поле ID ну давайте да ещё какое-нибудь поле добавим например когда был создан укажем что это дата ну или number stamp там можно любое указать здесь сейчас не столь важно ну и вот да как видите он изначально их подхватывает и теперь эта функция будет ожидать на вход аргумент у которого уже определён айдишник и определена дата создания то есть ещё раз за счёт вот этого extends даже само вот название этого механизма ограничение generic constraint оно нам говорит о том что мы загоняем объект в определённые рамки то есть вот у нас у юзера например есть уже айдишник и есть дата создания то есть мы можем его без проблем передать вот сюда в generic и Typeescриpt нам покажет какие поля нам необходимо заполнить и сейчас всё будет хорошо потому что эти поля совпадают с тем что мы указали в ограничении но если из объекта usеer мы эти поля удалим то в таком случае Typeesриpt начнёт ругаться что у нас есть несоответствие с ограничением которое установлено на этот Generic это тоже очень важный и очень интересный механизм который добавляет гибкости и позволяет нам создавать суперруниверсальные функции классы типы с такими настройками скажем так ограничениями типами аргументами которые нам необходимы это очень классно и даёт огромную гибкость ну и также ещё стоит здесь обсудить как работать с дженериками в классах механизм точно такой же указываем после объявления класса после его названия опять же треугольной скобочки объявляем сам Generic и потом внутри его используем можем использовать внутри методов можем использовать для каких-то полей вот сейчас он ругается на то что конструктора нет то что нам поле проинициализировать надо непосредственно здесь мы можем этот аргумент принять и просто присвоить через дис в общем здесь каких-то прямо особенностей нет с дженериками внутри класса мы работаем точно так же как в типах как в функциях то есть этотрик доступен внутри класса ну и наверное из того что интересно ещё можно показать дженерики мы также можем использовать в Reactкомпонентах на самом деле мало кто про это знает ну опытные ребята естественно знают начинающие и понятия не имели что так делать можно например у вас есть какой-то универсальный компонент select выпадающий список грубо говоря и вы хотите сделать чтобы value у него был универсальный вот так вот в пропусах вы объявляете t extend string то есть вы хотите чтобы это бы была всегда строка но эта строка может быть ограничена каким-то какими-то литералами или каким-то энамом перечислением вот и вот таким вот образом используете а потом для того чтобы это Generic передать вы объявляете компонент вот таким вот образом и потом в треугольных скобочках необычный синтаксис но вот так вот он работает может пере можете передать какой-нибудь enм туда например и Typeescript подхватит это значение и для value и для функции onchange кстати вот сейчас вы видите проект который мы разрабатывали на курсе Продвинутый фронтEND там мы суперглубоко тему тайпскрипта разбираем э курс несмотря на то что ему 2-3 года до сих пор на 99,9% актуальны если интересно всегда ссылочка есть в описании можно ознакомиться с информацией итак вернёмся к плейграунду тут на самом деле есть ещё что обсудить давайте лишнее удалим оставим вот это responsс оставим здесь один аргумент лишнее удалим длянериков также можно задавать не только ограничения но и какой-то дефолт так же как вы можете указать у функции дефолтный аргумент также вы можете указать дефолтный тип например дата равняется string в таком случае когда вы будете использовать IP Response Typeescript не будет требовать от вас обязательной передачи какого-то дженерика он по умолчанию возьмёт то значение в данном случае стринг которого вы указали но вы всегда можете его перезатерев явно передав туда какой-то в данном случае мы указали usеer typescript его сразу подхватил то есть мы могли дефолтом указать usеer generic бы нам указывать не пришлось и вот он пишет что property username не заполнено то есть он его подхватил тоже достаточно полезный механизм держите его на вооружении ну и наверное самая такая сложная тема для начинающих - это условные типы так называемые conditional types что мы здесь делаем сначала указываем ограничения если дата у нас расширяет юзера грубо говоря тут прямо вот так вот и можно читать ну это как тернарное такое выражение тернарный оператор то есть это прямо условие и если оно выполняется то мы возвращаем один тип в обратном случае возвращаем другой очень полезный механизм благодаря которому огромное количество библиотек утилитарных функций типов написано нереальную гибкость добавляет давайте посмотрим на каких-нибудь более простых примерах прямо на каких-нибудь базовых создадим тип который будет проверять является ли у нас переданный generic массивом добавляем generic и делаем здесь вот такую проверку если т у нас расширяет любой массив тут можно либо any указать либо unknown вообще не столь важно потому что эти данные никак не влияют на то что мы делаем ну и соответственно если расширяет массив указываем true в обратном случае false здесь могут быть не только булин значения там могут использоваться абсолютно любые типы ну и давайте попробуем вот объявить какие-нибудь константы и укажем например string уже typesриpt нам показывает что значение у нас может быть только false если мы укажем true то typesриpt будет ругаться он говорит что оно не может быть присвоено false если присвоим false то ошибка пропадает ну и попробуем заменить теперь укажем массив строк уже видим что Typesриpt показывает нам значение true для этой функции ну единственное название самой константы поменяем ну да все ошибки пропадают в общем с помощью вот такого вот простого условия с помощью кондиitional typйпа мы сделали простую проверку на то является ли тип массивом давайте ещё какой-нибудь примерчик посмотрим который не с булин значениями а с какими-то типами ещё над названиями особо думать не будем напишем random name generic ну и опять же такой же механизм t у нас extended расширяет ну пускай будетстринг в таком случае возвращаем объект у которого value будетстринг в обратном случае давайте вернём ну какой-нибудь такой же объект но пусть у него остаётся ну как как такая конструкция конечно я вот сейчас на этапе озвучки уже смотрю не особо смысл имеет но просто в качестве примера сойдёт ну если мы сейчас попробуем создать какую-нибудь константу указываем тип random name ну и передадим как раз внутрь string по идее у нас value сейчас должен быть строго захардконстринг ну да если мы сейчас передадим сюда какой-нибудь любой объект то он окажется внутри но здесь на самом деле да вот сейчас я смотрю пример не очень удачный потому что у нас в любом случае стринг бы подставился потому что мы genнериic там передаём пример не самый удачный но думаю суть вы поняли видимо потом я осознал когда я всё это записывал что пример не очень хороший вот здесь вот поменял extend string давайте заменим на extendse user ну в данном случае value string можно оставить ну или давайте на number заменим а вот здесь сделаем value string здесь передаём usеer ну и видим да что сработало первое условие если мы usеer уберём и подставим string срабатывает второе условие можем какой-нибудь здесь ещё не знаю number передать ну да тоже value string остаётся то есть он не видит соответствия типа с юзером он вот этот constraint условия не отрабатывает ну и соответственно возвращается то что после двоеточия если мы подставим здесь username string то видим что number условие отработало тип соответствует вот этому юзеру и всё хорошо по структурному как раз типу типизации тайпскрипта при этом если мы добавим ещё одно какое-то поле то условие также будет отрабатывать потому что концепция супертипов и подтипов то есть сейчас мы передали в randм name подтип для юзера и всё у нас хорошо отработало если мы заглянем на утилитарные типы внутри тайпскрипта который мы будем разбирать ну это такой набор базовых инструментов то мы увидим что здесь просто повсеместно используются эти условные типы never unknown - это то что делает Typeesриpt выразительным гибким языком с помощью которого мы можем творить любые чудеса сейчас это может выглядеть всё очень страшно непонятно но чуть позже я думаю все эти вопросы будут закрыты к дженерикам мы ещё вернёмся а сейчас переходим к важнейшей теме которая называется сужение типов представим что у нас есть вот такой вот тип мы объединили два множества но в каком-то участке кода мы хотим их разъединить и с числами работать как с числами а со строками работать как со строками на всякий случай уточню что здесь могут быть абсолютно любые типы такая задача встречается повсеместно когда вы работаете с Typeescript в документации описана целая пачка способов того как типы можно сужать сейчас мы быстренько по ним пройдёмся с какими-то моими пояснениями первый способ и такой самый простой представим что у нас есть вот такой аргумент который может быть number string или now и первый способ сзить наши типы - это сделать самые классические проверки через typпоof typeescript понимает эти проверки и в рамках каждого вот такого условного блока он будет понимать с каким типом мы работаем закончили писать условия видим что вот здесь вот за пределами этих условий у нас ARC равен если заглянем вот сюда здесь у нас естественно будет number и мы можем обращаться ко всем методам которые есть у number и то же самое со String можем там к какому-нибудь регистру преобразовать и так далее стандартные методы строк ну если подытожить если вам надо обработать какие-то отдельные сценарии со строками отдельные с числами или с другими типами используем для этого typeof это работать с примитивами разобрали первый способ через typпоoб через сравнение проверки на true здесь в принципе механизм очень похож давайте эту функцию скопируем назовём её FN2 вот эти вот условия уберём и теперь условия мы делаем не через typйпов а просто проверяем на какое-либо значение если у нас аргумент nл мы делаем проверку и обрабатываем кейс когда у нас аргумент именно равен на либо же наоборот мы можем сделать проверку и исключить n и undefed например и дальше работать уже с какими-то значениями при этом если мы пишем какой-то явный литерал например там 5 или какую-то строку 1 2 3 то именно этому значению и будет равняться аргумент либо какая-то другая переменная внутри этого условия при этом TPрипt умный он понимает что если у нас один аргумент равняется другому аргументу здесь да единственное кавычку уберём он видит что arc 2 у нас numberр и поэтому он понимает что arc1 у нас тоже numberр если они равны следующий пример уже менее очевиден до этого мы работали с примитивами теперь давайте поработаем с объектами есть два типа которые описывают информацию о каком-то пользователе о каком-то человеке у первого есть usернейм у второго нет и в функцию прилетает аргумент который как раз может быть либо usеer либо person сделать проверку через typeof мы здесь уже не не сможем потому что и в первом и втором случае у нас будет просто object здесь можно воспользоваться вот таким вот способом мы проверяем что внутри нашего объекта через оператор in содержится то или иное поле typeesриpt понимает что это поле у нас есть в юзере но нету в Прсоне и поэтому он автоматически выводит тип сужает его внутри соответствующего условия такую же проверку можем сделать например и по first name для person этот способ прямо запоминаем потому что он позволяет вам решать такие иногда не самые очевидные кейсы ну это прямо полезно иногда бывает ну вот если мы выйдем за пределы вот этих условий тип у нас опять становится usеer или person то есть в рамках условия мы его сужаем следующий способ работать только с классами вот здесь это очень важно не путать обычный тип объявленный через интерфейс или type и классы с которыми мы работаем ещё раз повторюсь этот способ будет работать только с классами и это использование оператора instance off здесь механизм в целом похож на typйпоof за тем исключением что мы работаем вот с объектами созданными через конструктор класса а не примитивами ну как видите вот здесь Typeesриpt уже определил что этот аргумент у нас BMW внутри условия ну и если мы сделаем else то он поймёт что здесь у нас тип уже будет Audi давайте ещё какие-нибудь методы в каждый из классов добавим чтобы точно увидеть что условия у нас отрабатывают как надо здесь добавим какой-нибудь Audi Driй здесь BMW Drive ну попробуем их вызвать в одном условии да автокомпeteт нам сразу подсказывает ну и во втором точно так же ну то есть видим что у нас всё работает при этом если условия мы убираем ну естественно Tapйpesриpt сразу начинает ругаться потому что не понимает где у нас какой тип сейчас рассмотрим один из таких важнейших самых часто используемых способов сужения типов для объектов вот он встречается прямо очень часто поэтому его тоже очень важно понять и запомнить называется discriminated unions давайте смотреть пример представим что у нас есть какой-то базовый интерфейс который задаёт какие-то общие правила и от него наследуются два других один Audi другой BMW и у них есть какие-то специфичные свои поля ну абсолютно любые здесь у нас BMW field здесь Audi Field просто сделано для примера из того что здесь прямо важно - это вот это поле type которое однозначно позволяет нам идентифицировать тот или иной тип обратите внимание что это литерал причём строго захардкоженный в одном случае BMW в другом Audi какие здесь ещё могут быть примеры например у вас блог и вы можете создавать статьи вы можете создать статью текстовую видеостатью какую-нибудь фотостатью ну различные варианты вот таким же образом вы будете с ними работать например у вас есть пользователи у них есть разные роли: админ менеджер там юрист пользователь обыкновенный и вы не просто добавляете поле с ролью для них а у вас и отличается набор полей для каждого из этих типов то же самое вы создаёте разные интерфейсы выносите общие какие-то поля в отдельный базовый интерфейс и наследуетесь от него и у вас будет отдельно type там usеer type adдмин typeменеджер здесь примеров можно придумать огромное количество ну и самое главное у вас в конце есть вот такой вот union тип как в данном случае Car который все ваши вот эти вариации объединяет ну и давайте посмотрим как вот с этим discriminated union со сжением типов работать представим у нас есть функция которая на вход принимает как раз вот этот car мы в условиях в sвичкейсе ну чаще всего switchкейс делают но можно if это обработать видим что при обращении к аргументу мы естественно вот эти специфичные поля не получаем мы получаем только базовые которые есть у всех и мы можем сделать проверку как раз по типу опять же повторюсь это может быть либо if либо switchкейс создаём первый кейс обрабатываем там случай для Audi ну и создаём точно такой же кейс но для BMW пробуем обратиться к аргументу внутри первого свичкейса видим здесь Audi Field обращаемся ко второму видим здесь BMW field ну то есть здесь уже ругается на то что Audi Field отсутствует то есть Typeesриpt сам по вот этому полю Type смог вывести нужные типы сузить их и правильно обработать обратите внимание что как мы выходим за пределы свичкейса у нас аргумент опять становится unionтипом ну здесь у нас естественно в дефолтном кейсе потому что оба кейса мы обработали выше если у нас появляется какой-то третий вид машины в данном случае Toyota мы заменяем у него поле type указываем новый тип вот в этом общем нашем Union типе то мы увидим что в дефолтном кейсе у нас появится Toyota так здесь по-прежнему Never потому что вот здесь я не указал Car да правильнее было бы сделать вот так использовать общий тип ну и да видим что здесь у нас Toyota здесь мы можем воспользоваться уже известным механизмом Exostive Check когда мы используем N для того чтобы он подсвечивал нам необработанные кейсы ну и давайте немного подытожим в данном случае у нас есть несколько типов которые объединяются в один через Union и для того чтобы нам их друг от друга отличать необходимо добавить какое-то поле которое однозначно будет каждый из типов идентифицировать в данном примере это поле type следующий раздел я вынес прямо отдельно э называется он Type Guuards в целом TypeGard - это любой механизм который позволяет как-то сузить типы но в данном случае я подразумеваю пользовательские тайпгарды которые мы пишем самостоятельно в виде отдельных функций опять же давайте рассмотрим базовый примерчик есть два интерфейса один для машины другой для человека ну и функция которая с ними как-то работает наша задача- сузить типы для того чтобы конкретно с каждым из этих типов поработать а можем написать условия проверки там по какому-нибудь полю ещё по чему-нибудь но представим что у нас в коде таких функций много и везде писать однотипные проверки не есть хорошо поэтому мы можем создать отдельный пользовательский tyйpeгард который будет нам эту проверку внутри себя грубо говоря инкапсулировать на вход такая функция получает любой аргумент это может быть перечисление каких-то вот например типов машин перечисление каких-то объектов может быть unknown внутри которого мы уже делаем проверку на какой-то отдельный тип например мы хотим проверить просто что у нас из набора машин та или иная является там BMW является Audi написать какую-то свою проверку под неё ну в данном случае давайте напишем Type guardard который принимает как раз два типа с которыми мы работаем и в чём здесь основная суть вы должны написать вот такую вот конструкцию в возвращаемом типе что вот этот аргумент его название из тип который вы хотите проверить в данном случае value is car если вы хотите сделать такую же проверку естественно меняете название и здесь делаете проверку на value is person сейчас typescript нам подсказывает что мы должны что-то из этой функции вернуть это должно быть булеан значение true или false которое будет подтверждать является ли value которое вы передали в функцию нужным типом на который вы хотите сделать проверку здесь может быть абсолютно любая проверка главное чтобы возвращалась trueue или false ну в нашем примере с объектами можем воспользоваться одним уже из известных нам способов воспользоваться оператором in и просто проверить что Max Speed и ширина два обязательных параметра у нас присутствуют внутри объекта вот таким вот образом и в целом этого будет достаточно ну опять же там в зависимости от кейса который вы решаете у вас могут быть и другие проверки здесь мы в качестве примера просто воспользуемся вот таким ну и для person делаем то же самое только проверяем уже на поле A и поле name всё тайпгарды наши кастомные самописные готовы теперь мы можем их в коде использовать так как нам необходимо ещё вот хороший пример до этого мы рассматривали механизм discriminated union но иногда нам в коде не нужно проходиться по всем типам каждый раз писать вот эту проверку ifype равняется там BMW делай это делай то и вот здесь вот хорошо когда у нас есть вот такое вот поле по которому мы явно какой-то тип можем идентифицировать и тогда Typeгаard получается прямо вот образцовым идеальным давайте добавим ещё один интерфейс для того чтобы было из чего нам проверять ну соответственно Typeга Guardard сделали принимаем BMW или Audi на вход ну и делаем проверку на то что V является BMW ну и вот здесь уже у нас прямо классическая проверка просто проверяем по типу если тип равняется BMW то возвращаем true в обратном случае false ну то есть здесь ещё раз уточню что мы можем воспользоваться любым методом сужения типов какой нам необходим можем даже по массиву там пройтись и каждый из элементов проверить здесь мы ограничены только нашей задачей ну и затем в коде вот через такое условие проверяем то что нам необходимо как видите Typeescript сразу подхватил что дата у нас является car а не person важно всегда помнить что Tapeesриpt умный если вы напишете здесь блок else Tpescript поймёт что вот здесь мы исключили car значит в else у нас всегда будет попадать person но это если у вас unниion между двух типов ну если у вас естественно unниion из многих типов то для каждого придётся делать проверку опять же это можно сделать в ИФах можно сделать switchкейс но в любом случае Typeesриpt поймёт с чем вы работаете в том или ином условии сужение типов очень важная тема с ней мы заканчиваем для начинающих опять же рекомендую всё что мы прошли повторить закрепить ну а мы двигаемся дальше переходим также к важнейшей теме преобразования типов итак преобразование типов подразумевает когда мы один тип явно или неявно превращаем в другой мы уже затрагивали явный и неявный вывод типов но преобразование немного другое давайте разберём в данном случае мы строку складываем с числом ну получается конкатенация на выходе мы получаем строку то есть здесь у нас произошло неявное преобразование изначально было число мы к нему добавили строку получили строку с неявным всё понятно здесь особо обсуждать нечего куда интереснее явное преобразование типа есть у нас объект для тейпскрипта - это просто объект с двумя полями если мы его явно вот так вот определили в коде то особо проблем нет за счёт структурной природы типизации тейпскрипта мы и так сможем этим объектом пользоваться и в нужных местахрипt поймёт что это у нас тип person потому что поля соответствуют структурной типизации он этот объект скушает куда интереснее если этот объект мы получили с бэкэнда через фч какою-нибудь или например распарсили Jon строку и объект вроде у нас тот но Typeesриpt не очень понимает что это за объект для него это просто обкт-то там полями которые он вывести не может потому что парсинг джесона произошёл в рантайме ну давайте рассмотрим пример представим сейчас что вот этот объект не объявлен в коде а он пришёл нам например в виде JSON строки мы её распарсили и это у нас просто object без конкретного набора полей и мы хотим Тайpeскрипту объяснить что это person для этого есть такой механизм как type указываем ключевое слово S и потом указываем название типа в которой мы хотим скастовать исходный объект ну как видите теперь Type scриpt подхватил он понимает что это person этот механизм крайне небезопасный и позже я расскажу почему в продакшн-коде его лучше не использовать ну использовать аккуратно в некоторых случаях он будет вам необходим давайте попробуем добавить ещё одно поле оно обязательно обратите внимание по идее Typescриpt должен его запросить но за то за счёт того что мы явно преобразовали этот объект к типу Tpeesриpt это начинает игнорировать потому что он думает что это поле уже есть при этом здесь есть тоже такие неочевидные моменты если мы добавим какую-нибудь хрень в этот объект то Typeesриpt нас попросит добавить поле с паролем давайте его добавим и при этом как видите вот эта хрень у нас остаётся ошибка пропадает но как бы Tapйpesриpt не ругается то что у нас вообще какое-то левое поле появилось в объекте типа person которое изначально не объявлено механизма вот такого явного прямо преобразования одного типа в другой два ну как бы механизм этот один называется он type assertion но синтаксис разный вместо вот такого вот s person вы можете использовать перед самим типом вот такие вот треугольные скобочки это не generic здесь важно понимать это именно вот такое явное указание здесь единственное у вас если в конфиге прописан JSX React он будет воспринимать это как jксовские теги но если вы JSX парн отключите то он спокойно такое съест в принципе этот способ лучше не использовать потому что все мы сейчас используем JSX лучше преобразовывать через ключевое слово S но ну просто про это важно знать тоже если вы встретите такой синтаксис где-то чтобы вы не пугались и не пытались понять что это такое при этом какие-то явно глупые преобразования сделать вам tapesриpt всё равно не даст допустим у вас есть строка и вы пытаетесь прямо эту же строку приобразовать к нам numberберу естественно такого не пройдёт ну потому что здесь прямо явная ошибка на лицо typpeesрипt это заметит всё равно есть способ это обойти и иногда в каких-нибудь конфигах тестах это всё же необходимо делать э есть вот такой вот хак вы сначала преобразовываете unnown и только потом преобразовываете к number очевидно что в продакшн-коде так делать не стоит и понятно почему это сразу же приведёт к ошибке вы попробуйте использовать какой-то метод или поле которое есть у number у стринга его не будет ну естественно вот такую вот ошибку словите думаю здесь даже останавливаться на этом не стоит это должно быть понятно как дважды два но опять же в тестах в конфигах каких-то внепродакшн-коде иногда это бывает полезно просто подогнать одни данные под другие ну и здесь стоит рассмотреть ещё один механизм который появился относительно недавно в версии 4.9 давайте сейчас объект продублируем и здесь используется ключевое слово satisfies в отличие от S оно не преобразовывает объект оно лишь проверяет соответствие то есть прежний объект у нас останется таким как есть кн он явно не преобразуется но при этом Tesриpt проверит структурно совпадают ли эти объекты если мы с зажатым Ctrl или Command направим здесь мы увидим сырой объект вот с этими полями то есть изначальный если мы направим на object 2 здесь мы увидим уже person то есть в этом основное отличие так я говорил что я расскажу когда допустимо использовать S понятное дело что в продакшн- коде его лучше не использовать так как это по сути в другой обёртке который немного побезопаснее но тоже может привести к ошибкам итак вполне ок использовать каких-нибудь конфиговпак вид там настройка тестов где не особо хотите запариваться точно знаете в каком месте что есть что и вам надо явно преобразовать одно в другое в целом ок тоже можно обойти сделать по-человечески но иногда допустимо в тестах поголовно встречаются кейсы когда вам какой-нибудь кусок данных куда-то надо подставить весь тип вам воспроизводить не хочется вам достаточно воспроизвести кусочек вы его слепили и преобразовали тоже ок иногда бывает нужно при работе с HTML-элементами один другой преобразовать по какой-то причине в утилитарных функциях это когда вы пишете какие-то хелперы для работы с кодом определённые сейчас мы их рассмотрим я вам прямо на примере покажу ну и бывают случаи когда тест не может вывести тип сам это опять же преобразование JON FCH ну опять опять же сейчас рассмотрим на примере пока что просто вот это всё фиксируем так ну и сразу давайте перейдём к примеру используем стандартный Gson Pars передаём туда строку пусть будет объект с полем H здесь не принципиально так ну и смотрим сразу на результат и видим что по умолчанию Typeescript воспринимает это как any вот если наведём то увидим что функция возвращает any и типизировать это никак нельзя ну то же самое в принципе будет если мы воспользуемся давайте отдельную асинхронную функцию создадим ну и чтобы получившийся результат спартить к джисону вызываем у получившегося ответа функцию JON добавляем потому что это асинхронный промис у нас получается ну и как видим что у нас any завёрнут в промис ну и здесь если направим тоже видим par data any вот для таких случаев в принципе type подходит здесь мы можем явно указать что мы ожидаем это будет также небезопасно но важно понимать что когда речь идёт о каких-то сторонних данных будь токэнд или какая-то строка речь о безопасности в принципе не идёт нам там может прийти всё что угодно здесь уже всё зависит от контракта с которым вы работаете ну и вот воспользовавшись нужным инструментом мы можем написать такую утилиту helpпер который за счёт преобразований за счёт дженериков будет возвращать нам типизированные даты вот здесь мы уже делаем явный type assertion преобразовываем generнерику которую мы передали и теперь для того чтобы типизированно работать с GSON Pars мы используем нашу утилиту которая грубо говоря внутри себя инкапсулирует стандартный GSON Pars здесь мы передаём что мы ожидаем на вход ожидаем мы например объект с типом ну и тогда нам будет Tpescriptрипt этот тип подхватывать но опять же важно понимать что это не будет безопасным в целом нам ничего не мешает здесь вот так вот просто person указать и это тоже сработает то есть по дене мы можем подставить всё что угодно давайте ещё посмотрим такой более явный пример у есть функция которая возвращает набор ключей объекта и она всегда возвращает массив стрингов то есть не чёткий набор литералов который есть у этого объекта а именно массив стрингов давайте сейчас посмотрим на примере как это дело можно типизировать давайте скопируем вот эти вот поля с ними мы и будем работать добавим их здесь заполним не забываем объект сделать SCONT чтобы поля были редонли чтобы их было нельзя менять видим что у нас все эти поля в объекте зафиксировались то есть этот объект у нас хранит по сути ключи типа person он нам чуть позже пригодится будем использовать его внутри функции теперь давайте попробуем из какого-нибудь объекта уже готового у нас извлечь ключи и посмотрим что получится вынесем результат в отдельную переменную ну и видим да что по сути у нас просто массив стрингов а что там за стренги да фиг его знает а нам необходимо чтобы у нас возвращались именно вот эти значения AG username password ну то есть вот примерно вот в таком виде то есть это должны быть литералы именно строковые массив вот таких вот литералов чтобы мы могли типизированно с ними работать ну и давайте реализуем функцию в которой вот как раз за счёт type мы такую типизированную версию object реализуем обязательно добавляем generic потому что мы не знаем с каким именно объектом мы будем работать добавляем аргумент здесь друзья я опечатался уже на монтаже заметил там должен быть не массив а просто объект чуть позже я это замечу и поправлю и теперь самое важное что должна возвращать эта функция возвращать она должна массив но не просто массив строк а массив вот как раз типизированных ключей здесь мы воспользуемся оператором KOF о нём я более подробно расскажу но если кратко это оператор который позволяет извлечь ключи из объекта сейчас этой информации достаточно а чуть позже мы его подробно разберём ну и уже из функции возвращаем опять же object передаём туда объект который мы ожидаем на входе и уже результат выполнения этой функции мы явно преобразовываем к тому что ожидаем то есть массив именно ключей объекта ну давайте сразу вызовем эту функцию и посмотрим как она нам типизировала передаём туда тот же самый обкт на котором мы изначально экспериментировали да вот здесь я уже заметил что ожидается там не массив а просто объект сейчас уже почти всё хорошо но единственный момент сейчас дата у нас это самый максимально общий Generic без каких-либо ограничений мы и строку можем туда передать и число и булиан и нам необходимо написать constстraint который ограничит использование дженерика в данном случае он обязательно должен быть объектом чуть позже я про эту конструкцию именно с Object ещё расскажу буквально пару минут ну пока считаем что просто в качестверика у нас должен быть объект нам этого достаточно смотрим на результат видим что константа к у нас - это массив ключей person то есть в данном случае если мы будем писать какую-то функцию которая на вход ожидает ключи мы без проблем сможем их туда передать вот это прямо валидный кейс когда type assertion можно использовать безопасно это даже вам поможет и в десятый раз ещё повторю просто так преобразовывать один тип в другой в продакшн-коде не стоит по-хорошему лучше прямо вообще линтер какой-нибудь подключить который будет запрещать вам это делать использовать any использовать преобразование через type и так далее всякие тес игноры запретить про это ещё поговорим думаю с этим понятно также очень важная тема которую стоило обсудить и очень важно понять но пока мы не ушли слишком далеко стоит разобрать определённую ловушку с которой сталкиваются наверное 99% начинающих и у них в коде потом возникают ошибки они не могут понять где они это вот эта конструкция stay Extendse object здесь есть один нюанс часто бывает необходимость указать функции где-то то что мы на вход ожидаем объект любой объект неважно какой там аргументом женериком возвращаемым значением ну бывают такие кейсы и велик соблазн указать либо просто пустой объект либо написать вот так вот object с большой буквы но давайте посмотрим как это воспринимает Tapeescript обратите внимание здесь всё разбито на три кейса: Object с большой буквы object с маленькой буквы и empty Object - это когда мы просто указываем пустой объект обратите внимание что в случае с пустым объектом он у нас принимает практически всё: и пустой объект и объект с полями и число и строку и функцию вот n undefined не принимает как видите Typeesриpt ругается но при этом всё остальное он с радостью кушает при этом ситуация с Object с большой буквой ровно такая же ситуация в том что вот Object - это глобальный тип который представляет любой объект который создаётся через construtor object а через него создаются и массивы и функции и примитивы new number new string и получается что через этот конструктор создаётся всё ситуация с пустым объектом здесь похожа разрешается всё кроме налы undefined важно понимать что здесь не подразумевает этот синтаксис пустой объект вот и использовать их в таком случае неправильно функция будет у вас ожидать на вход всё что угодно кроме undefined а вот object с маленькой буквы уже то что нам нужно он означает объект помним что в ТС объекта это и массивы и функции любые ссылочные типы данных но этот тип не разрешает примитивы не разрешает симбол не разрешает бун значения не разрешает n undefed но при этом разрешает любые объекты в том числе массивы функции даты и так далее то есть ещё раз когда вы хотите сделать вот такую вот конструкцию написать constraint ограничения только на объект то правильней будет использовать object с маленькой буквы двигаемся дальше я уже много раз сказал в этом выпуске что Typeescриpt - это выразительный гибкий язык в нём столько инструментов подходов механизмов которые позволяют писать гибкий переиспользуемый насыщенный код и решать все задачи которые могут у вас возникнуть в тест есть два оператора которые супер часто используются: Typeo и KOF давайте их по очереди разберём частично мы их уже затрагивали но теперь зайдём поглубже начнём с тайпов здесь есть очень важный нюанс есть typeof который используется как оператор в JavaScript мы можем сделать tyпов от любого примитива символа массива объекта и получить тип это будет там object number string buan и так далее важно понимать что это инструмент вы можете прямо в режиме работы приложения вызвать там какую-нибудь функцию получить тип той или иной иной переменной функции там объекта ещё чего-то в ТС есть свой оператор тайпов который позволяет извлекать типы это немного другое вот здесь сразу важно понять что инструменты немного разные он также возвращает тип извлекает его но это не runтайм инструмент это именно про типизацию ну и сейчас естественно мы будем разбирать тайпов именно тайпскриптовый для типов базовый сценарий у нас есть какой-то объект вот таким вот образом объявленным мы хотим получить из него тип вывести все поля вытянуть их и поместить в отдельный typйe для этого мы используем tyпопов очень часто используем инструмент как видим всё подтянулось автокопт нам сразу подсказывает какие поля есть у person это работает не только с объектами работает в принципе с любыми значениями давайте посмотрим как это сработает на обычной строке как у нас выведется литерал пусть это будет цвет создаём константу color передаём red и попробуем вывести её в отдельный тип создаём type используем type ofof и вытягиваем его из константы color теперь попробуем создать другую константу назовём её green например и как видите если мы заюзаем тип red color то на Typeescriptрипt нам не даст потому что он извлёк вот этот литерал и он строго равняется red при этом здесь тоже есть нюансики сейчас это у нас константа если мы заменим на LED то ошибка вдруг попадает потому что в случае с LED мы значение всегда можем переопределить поэтому вот такие вот литералы всегда важно держать константными если вы хотите их строго зафиксировать что мы можем делать э с тайпов ещё допустим у нас есть функция частый сценарий когда у нас есть эта функция какая-то функция но её тип нигде отдельно не выведен и мы хотим получить с какими данными она работает какие аргументы ожидает что возвращает то же самое что и объект пишем тип используем typeпоof только вместо объекта используем уже функцию get data вот таким образом нам вернётся как видите стрелочная функция у неё есть один аргумент и тип возвращаемого значения number этот тип мы можем использовать теперь в типизации чего-либо а что если нам нужно получить не всю функцию а например тип аргумента или тип возвращаемого значения естественно в Тайпскрипте это всё уже предусмотрено есть даже стандартные инструменты которые позволяют это сделать давайте создадим тип вот такой getur value и в тайпскрипте есть стандартная утилита которая называется return type это ути type так называемый чуть позже мы их будем рассматривать но пока считаем что это такой тип как функция которая позволяет вытащить нам тип возвращаемого значения функции здесь то же самое передаём туда функцию type of get data и с помощью вот этой утилиты мы достаём вот тот самый numberр который эта функция нам возвращает вот обратите внимание если здесь заменим настринг то будет стринг очень похожим образом мы можем вытащить и аргументы из функции давайте поменяем название у типа здесь конструкция похожа только вместо return type мы используем utilтиity type parameters ну как видите он нам вытащил такой массивчик в котором один объект ну если мы добавим ещё какой-нибудь аргумент давайте ещё третий добавим то увидим что у нас массивчик из трёх значений это объект user a number и value string в общем давайте насчёт тайпов подытожим есть два вида: runй который используется в JS и тсовская версия тайпов для извлечения типа именно типа который тайпскриптовый не знаю как тут по-другому ещё сказать и работать это может с любым типом данных будь то функция массив или объект давайте поговорим теперь про KOF в принципе мы его уже затронули тут сильно много не скажешь он позволяет доставать ключи с этим думаю понятно ну из интересного что мы можем делать мы можем typeof и kf комбинировать например у нас есть объект мы делаем от него typeof достаём тип и потом сразу же от этого получившегося типа мы берём KOF то есть комбинируем скажем так операторы вместо того чтобы выносить тип в отдельный type person мы вот таким вот образом сразу эти ключи достаём ну и сразу рассмотрим такой практический пример из реального мира так сказать есть функция которая из объекта по ключу достаёт какие-то данные и вот здесь вот важно правильно типизировать ну понятное дело что добавляем здесь generic для объекта который может быть абсолютно любым и добавляем второй Generic который как раз мы сразу напишем так чтобы он автоматически доставал ключи из первого дженерика то есть здесь вот поставьте на паузу и прямо вникните в то что мы делаем мы создаём второй Generic который берёт KF от первого дженерика ну и получается так что мы передаём объект из него автоматически достаются ключи и автокомплит нам все эти ключи подскажет и ещё и правильно типизирует ну и нужно ещё написать саму логику функции здесь в принципе ничего сложного нет возвращаем просто по ключу нужное значение из объекта понятное дело что здесь может быть логика не настолько простая чтобы просто достать какие-то данные можно какие-то проверки ещё добавить усложнить как-то какую-то логику ну я показываю вам просто суть и обратите внимание что за счёт того что мы передали объект уже автоматически у нас второй аргумент вывелся и автокомплит нам подсказывает какие здесь есть поля то есть что-то левое мы сюда передать уже не сможем tapйpesриpt сразу начнёт ругаться уже на этапе монтажа заметил тоже как эту функцию ещё можно улучшить можно было указать тип возвращаемого значения указываем generic T и по ключу достаём из него нужное значение вот таким вот образом для ребят которые с тейпскриптом уже работали наверное сейчас информация достаточно легко вся даётся я представляю сейчас какая каша в голове у тех кто с этим сталкивается первый раз и кто только начинает изучать а ну тут ребята важно понимать что это всё вам сразу не пригодится воспринимайте это как справочник посмотрели запомнили что что-то такое есть отложили до тех пор пока не пригодится с этим разобрались двигаемся дальше и сейчас мы разберём два оператора Optional которые есть и в JS но поддерживается не во всех браузерах и NAL оператор начнём с Optional оператора который используется просто повсеместно это оператор который обозначается как вопросительный знак с точкой и обратите внимание есть поле Person при этом адресы внутри опциональные то есть его может и не быть и соответственно когда мы хотимся обратиться к какому-то полю внутри объекта внутри адреса в данном случае это поле стрит может случиться такое что адреса у нас нет и мы уfed пытаемся получить какое-то поле естественно сразу будет ошибка и вот оператор позволяет добраться до какого-то внутреннего поля глубоко и при этом сделав это безопасно если на каком-то участке этого пути у нас встречается now или undefined то нам просто вернётся undefined и в исключение у нас приложение не свалится лучше всего конечно же сразу наглядно посмотреть вот есть такой же у нас person у которого адрес поля не обязательно его может и не быть и внутри есть стрит давайте попробуем из адреса достать эту улицу ну уже видим что сейчас Tapescript ругается и он говорит что у нас адрес может быть undefined но допустим мы что-то неправильно типизировали представим что Typeescript сейчас не ругается у нас есть какой-то TSNor что-то где-то мы упустили и ошибки никакой нет давайте попробуем запустить и посмотрим что будет ts игнор так же как и не используем только на этапе обучения в продакшене не стоит так давайте запустим нажимаем Run да забыли функцию вызвать давайте её ещё вызовем передадим туда объект ну и как видите да естественно у нас свалилось с ошибкой нельзя прочитать поле уfed что логично естественно Tescript нам эту ошибку подсказывает и чтобы её убрать необходимо добавить как раз optional оператор в таком случае до этого поля выполнения просто не дойдёт и эта цепочка она может быть сколь угодно большой поэтому оператор называется именно optional chainнин то есть это именно цепочка можем заходить настолько глубоко насколько это требуется про этот оператор я думаю так или иначе практически все слышали кто с JS работает думаю здесь прямо подробно останавливаться не надо но есть некоторые нюансики про которые не все начинающие знают как работать с полями тут в принципе всё понятно как работать с функциями и с массивами на самом деле здесь может быть точно такая же ситуация представим что у нас есть необязательный какой-то калбэк давайте попробуем его вызвать продублируем вот эту строчку вызываем getage ну опять же да видим что Typeescript нам ошибку допустить не даёт он нам подсказывает что может быть undefined поле но для того чтобы нам безопасно эту функцию вызвать мы перед скобками можем также добавить optional chain operator как видите у нас просто в двух случаях вернётся undefined и функция вызвана не будет с массивом это работает также давайте посмотрим чуть-чуть синтаксис будет отличаться в том плане что нам перед скобками нужно будет этот оператор поставить дублируем consol log обращаемся к массиву и попробуем достать например там нулевой какой-нибудь индекс то же самое видим что Typescript нас от ошибки спасает используем Optional Chaining Operator вопросик с точкой перед квадратными скобками таким образом мы безопасно обращаемся к элементу массива по индексу с Optional Chaining разобрались теперь давайте посмотрим на эта штука что-то типа что-то типа tignг в общем затычка такой хак для того чтобы просто убрать ошибку и заткнуть Typeesриpt что он делает он позволяет точно также получить доступ к к объекту но убирает ошибку которую вот мы чуть ранее рассматривали что типа невозможно получить поле undefined он просто затыкает рот тайпскрипту то есть подразумевается что мы используем этот оператор когда мы точно уверены что в этом объекте у нас есть адрес но по факту в продакшн-коде это лучше не использовать сегодня мы уверены завтра кто-то другой код переписал там уже этого поля нет стрельнула ошибка пользователь потерял деньги мы в итоге накосячили точно так же как STPE asion использо где-нибудь в конфигах в тестах вполне окм никаких в продакшн-коде в бизнес-логике естественно лучше не использовать но опять же давайте глянем примерчик рассматриваем тот же код часть полей у нас не оббязательная видим ошибку что адрес может быть undefined но вместо вопросительного знака используем восклицательным как видите ошибка ушла мы её заигнорили то есть это что-то типа ТС игнора того же но если мы запустим сейчас выполнение то сразу же получаем ошибку что Property Street мы не можем прочитать и он defineed то есть по сути относим этот оператор к тому же разряду небезопасных инструментов ну и как я уже говорил бывают сценарии когда это валидно использовать но это не сценарий продакшн-кода по-хорошему тоже добавить линтер в ваш проект чтобы никто из разработчиков такое не использовал когда это валидно например вы настраиваете VPAC config какой-нибудь или любой другой конфиг у вас там есть типы в которых некоторые поля undfined но вы точно знаете что где-то раньше в какой-то цепочке функции вы этот тип определили например там какой-нибудь деф-сервер внесли какие-то данные туда определили порт и вы точно знаете что в этом месте если вы заюзаете этот оператор у вас ничего не сломается ничего не свалится да и даже если сломается вы это всё заметите на этапе разработки то есть потому что это не продакшн код то же самое в тестах у вас есть какой-то тест есть типы в которых какие-то поля опциональные но вы точно знаете что вы где-то заранее это поле определили оно у вас не пустое в таком случае чтобы ошибку просто убрать чтобы всё у вас нормально прогонялось вполне валидно заюзать этот оператор всё будет хорошо и в десятый раз повторюсь в продакшн- коде этом лучше не использовать это приведёт вас к ошибкам лучше использовать optional chainнинг оператор добавить лишних пару условий убедиться в том что это поле есть дописать какие-нибудь я не знаю обработчики на случай если этож если этого поля нет и как-то так с кодом работать это безопасный вариант переходим к следующей теме которая хоть и достаточно маленькая и простая но тем не менее включает в себя определённые подводные камни её определённо стоит разобрать разбирать будем перечисление анижеена намы итак смотрим сразу на примерах есть у нас вот такой вот объект в котором содержится название цветов заранее определённый набор и когда мы указываем где-то эти цвета например хотим бэкграунд задать мы не просто указываем строку R а мы достаём её из заранее определённого поля из заранее определённого набора таким образом в случае чего мы можем в одном месте поменять это значение если вдруг нам по какой-то причине это понадобится и оно поменяется сразу во всех местах но с таким объектом в классическом сценарии есть определённые проблемы если мы сейчас попробуем сделать какую-то функцию которая на вход ожидает цвет то передать туда color как тип мы не сможем ну давайте сейчас сразу посмотрим что я имею в виду вот такой вот конструкцией мы воспользоваться не сможем такие перечисления и намы обычно называют или полностью большими буквами как констант или просто с большой буквой чтобы отличались от обычных переменных и здесь важный момент даже если мы используем уже известный нам typeof мы получим не тот результат который мы ожидаем мы не получаем Union из необходимых цветов мы получаем прямо сам объект в котором все поля перечислены то есть это решение нам не подходит там есть способ как работать с константами с вот с такими вот объектами как с инамами как с перечислениями чуть позже я покажу это наиболее правильный способ но давайте покажу нативные тайпскриптовые по работе с инамами объявляется он вот таким вот образом вместо type вместо интерфейс вместо const да давайте пока закомментируем и вот таким вот образом через равно мы объявляем все значения которые в этом энаме должны быть набор любых значений которые по какой-то логике объединены цвета размеры роли пользователя типы статьи и так далее ну и когда мы эту функцию будем вызывать мы можем сразу вот так вот через точку передать необходимое нам значение и всё будет хорошо то есть мы храним все цвета в каком-то одном месте и вот так вот удобно их используем но давайте попробуем запустить код и посмотрим что у нас будет в результате очистим логи нажимаем run единственное нам надо будет здесь вкладку справа в результате переключить нас интересует скомпилированный JavaScript и посмотрите что здесь происходит вместо обычного объекта у нас создаётся функция внутри этой функции заполняется объект потом эта функция вызывается и туда передаётся вот это вот заранее через вар объявленный объект ну то есть конструкция такая достаточно странная учитывая что Typeescript у нас - это только протипы здесь и - это вот единственная такая конструкция которая в результате компиляции даёт какой-то JavaScript код причём достаточно странный давайте раскомментируем объект справа мы видим что объект компилируется как обычный объект у нас просто color и со своими значениями и у Энама на самом деле можно вот эти значения вообще не указывать давайте посмотрим как это будет выглядеть в результате компиляции видим что по умолчанию там тут подставляются числа по порядку 0 1 2 при этом если где-то здесь мы вставим какую-нибудь пятёрку первые два у нас останутся по порядку третья будет пятёркой если мы добавим ещё какое-то значение давайте например yellow то оно будет идти уже отталкиваясь от предыдущего значения 5ше то есть ну немного тоже не самое предсказуемое поведение если этого не знать можно тоже на какую-то особенность какой-то нюанс в кодепороться при этом я там смотрел одно видео автор рассказывал о том что пользоваться инамами хорошо потому что в объект можно взять и добавить новое поле но на самом деле Typeescriриpt вам не даст добавить такое поле потому что изначально в константном объекте оно и не объявлено если вы сделаете LED то да скорее всего вы сможете добавить в случае с константой не сможете поэтому преимуществом это в случае намы не является но что вы можете сделать - это взять и перезатереть э какое-то значение которое в Col уже существует если вы заранее не определите этот объект как константный через SC const и не сделаете полярен вот тут уже да тут определённое преимущество нама есть если про эти нюансы не знать можно ну тоже ошибок наделать но в целом если вы человек грамотный обучились тайпскрипту и используете SCONST то объект даст вам только преимущество перезатереть какое-то поле вы уже естественно в этом случае не сможете итак мы рассмотрели нам в котором значения являются строками рассмотрели и нам в котором значение являются числа которые заданы по умолчанию или определены нам нами вручную но есть ещё так называемые константные энамы давайте посмотрим как они работают объявляются они вот так вот перед словом добавляем const лишнее давайте пока уберём как видите вот эта громозская конструкция с заполнением объекта у нас ушла и просто в место где у нас используется color 2 у нас подставилась строка то есть по сути компилятор просто подменяет все места где у вас используются константные нам на строки которые определены заранее ну строки или значения то есть по сути опять же с Инамом происходит здесь ситуация при которой мы работаем не только с типами но и на выходе у нас получается какой-то скомпилированный код теперь давайте поговорим про то как полностью заменить инамы обычными константными объектами нам необходимо вывести тип который будет называться так же как и константа и с помощью вот такой вот конструкции его получить на первый взгляд эта конструкция выглядит страшно но на самом деле она суперпростая давайте справа налево её прочитаем то что в квадратных скобках по сути мы просто получаем ключи объекта в левой части по этому ключу мы достаём значение из этого объекта то есть если посмотреть на сам объект то мы получаем то что написано мелким шрифтом Red green blue то есть сами значения объекта не ключи а именно значения эту запись можно сделать посимпатичнее мы создаём такой утилитарный тип который называется value of с generриком ну и вот эти вот все махинации с извлечением типа с извлечением ключей мы делаем внутри него и теперь когда мы достаём тип из объекта мы не пишем вот эту громозскую конструкцию а мы пишем просто value of и достаём тип из объекта type of color ну конструкция всё равно достаточно громозкая но в любом случае читается она лучше таким образом мы получаем обычный константный объект джаваскриптовый который компилируется у нас так как он должен компилироваться в обычный объект ничего лишнего в код не попадает итоговый но при этом мы получаем все преимущества энама то есть мы можем использовать его и как тип и как значение и при этом не можем изменять значение внутри этого объекта ну и чуть позже я сейчас сравнительную таблицу всех трёх способов enum констм и обычный object выведу и посмотрим чем они отличаются более детально но в любом случае с инамами так или иначе вам придётся сталкиваться давайте код немного откатим назад покажу вам ещё одну такую уникальную фишку которая есть в наме двустороннее связывание называется двусторонний доступ мы можем обратиться к Энаму вот таким вот образом к объекту вы так обратиться не сможете мы по значению получаем ключ то есть в квадратных скобках мы указываем именно значением давайте запустим ну вот как видите мы получили R то есть по значению ноль мы получаем ключ Red вот как видите можем и Blue получить и yellow таким образом при этом очень важный момент это работает только для числовых значений если вы работаете со строками так сделать уже не получится в общем много подводных камней о которых надо знать и давайте ещё взглянем на один примерчик как я уже говорил Typeesриpt - это структурный язык у нас вот может быть описан какой-то inлайн тип вот таким образом два разных типа но с одинаковыми полями таким образом и при этом вот эта функция за счёт структурной природы тайпскрипта спокойно оба этих типа примет потому что у них совпадают поля совпадает сигнатура и это ок за счёт этого Tйpesскрипт получается гибким и выразительным но с инамами так не работает и об этом тоже важно помнить и и однажды это может стрельнуть вам в ногу как-то мы переписывали один большой проект на микросервисы микрофронты и вот там у нас было очень много инамов и когда начали всё это делить за счёт того что вот эта структурная типизация отсутствует возникали проблемы при переносе кода и пришлось всё это переносить на константные объекты как видите если 1 принимает эта функция том 2 она не принимает хотя с точки зрения структуры они абсолютно одинаковые возможно это и не минус но об этом тоже надо помнить и это добавляет такой когнитивной нагрузки все типы интерфейса работают по структурной типизации а у Энама она иная закинул в чат GPT промт в котором я попросил сравнить обычные нам constam и const object ну давайте прямо по строчкам пройдёмся видим что constam у нас недоступен в Runime потому что на выходе там нет никакого объекта там есть только стройки которые заранее подставлены самый маленький размер бандла итоговый вес файла который вы получите в результате компиляции будет у const потом будет у const потом будет у обычного энама потому что там создаётся функция создаётся переменная эта переменная заполняется потом вызывается функция функции присваивается пустой объект это конечно относительно мелочи но если у вас очень-очень-очень много энамов в очень-очень большом проекте наверное там какие-то килобайты могут из-за этого набегать ну типизация да самый сложный здесь вариант типизации - это в const object то есть там прямо тип надо выводить но это небольшая проблема двустороннее отображение есть только в инамах но это полная хрень если вам вдруг такое понадобится в каком-то суперредком узком кейсе то вы легко пишетехпер который с объекта точно также будет доставать по какому-то значению ключ это не проблема ну и под удобством я не очень понимаю что здесь имеется в виду в принципе они все достаточно удобные но насчёт const object правильно написано что это простое гибкое решение и даже в документации тепскрипта написано что на данный момент лучше использовать const object ну а мы двигаемся дальше не настал момент поговорить про важный вопрос который всех интересует начинающих особенно разница между типом и интерфейсом давайте смотреть по большей части особенно для начинающих сейчас можно считать что как таковой разницы нет но есть детали которые всё-таки отличаются например для того чтобы объявить какие-то литералы нам необходимо воспользоваться типом также для того чтобы явно указать какие-то примитивные значения тоже воспользоваться типом придётся с интерфейсом вот такую запись вы сделать не сможете например вы хотите сделать какой-то алиас для лучшей семантики айдишники у вас в проекте типа number и вы отдельно выносите вот такой вот тип ID то есть делать это придётся вот таким вот образом дальше как мы работаем с наследованием с расширением типов представим что у нас есть какой-то базовый тип ну в котором есть например несколько полей пусть это будет usрнейм и какой-нибудь ну пусть будет дальше у нас есть несколько типов например там usеer person adдмин,менеджер похожий кейс мы уже разбирали сегодня и он вот как раз расширяет вот этот базовый тип в случае с интерфейсом мы используем extend получается у нас вот эти вот поля username age они как бы добавляются к user ну тут по по слову extends уже всё понятно с типом в принципе мы можем сделать то же самое только воспользоваться интерсекном с точки зрения механики как бы действия у нас немного разные с точки зрения результата они будут абсолютно равны те поля присоединятся просто к новому полю с паролем потом с интерфейсами есть ещё один нюанс давайте сейчас вот эти названия уберём и оставим то есть видим что у нас сейчас в рамках этого файла два интерфейса с одинаковым названием если мы попробуем создать объект мы увидим что они объединились то есть поля первого и поля второго объединились по названию если мы заменим на тип так сделать уже не получится потому что тип должен иметь уникальное название то есть объединяться в данном случае они не будут и каждый тип должен быть уникальным придётся сделать вот так то есть здесь тоже с интерфейсами важно про это помнить что они будут как бы подмёрживаться друг к другу потом следующий момент если мы хотим объявить кортеж кортеж - это массив фиксированной длины с заранее определённой структурой то есть в данном случае мы говорим что у нас будет массив из трёх элементов где первый элемент - это numberр второйст и третий обязательно пятёрка как видите сейчас Typeescriриpt не ругается но если один из элементов мы меняем на тот что не соответствует изначальному объявлению типа то естественно Tappeescriриpt сразу все ошибки подсвечивает для этого тоже используем type как пример использования такого кортеджа - это use state в реакте где первым элементом нам всегда возвращается сам state а вторым элементом массива нам возвращается функция которая этот stateт меняет ну здесь понятное дело что стрин я просто захардкодил по-хорошему здесь generрик должен быть для того чтобы всё это универсальное было но смысл думаю я вы поняли следующий момент он про производительность работы с интерфейсами мы вот рассматривали пример вот с этим вот extends и объединением с типами в документации явно написано что предпочитаются интерфейсы перед intersection то есть вот такая вот запись с extendse она более предпочтительна и Typeescриpt там под капотом будет выполнять меньше работы и как бы работать это будет лучше то есть здесь это тоже важно помнить и и предпочтительно для обычных вот таких вот операций с расширением использовать extends дальше когда вы хотите объявить какую-то функцию опять же здесь удобнее использовать type на самом деле через интерфейс функцию тоже можно объявить но запись там будет намного-намного страшнее давайте посмотрим как это будет выглядеть назовём FN2 интерфейс ну и сама запись будет выглядеть вот так в принципе похоже в принципе похоже но согласитесь с типом это выглядит более прозрачным да вот здесь тем более будет нестрелочная функция вот так вот через двоеточие возврачаемое значение указываем ну и давайте сейчас попробуем создать и через первый тип и через второй интерфейс функции ну убедиться что они одинаковые объявим здесь стрелочную функцию которая ничего не возрачает ну да давайте здесь ещё сделаем чтобы явно было заметно стринг какой-нибудь в качестве возвращаемого значения ну и то же самое сделаем во второй функции ну и там и там видим что ожидается возвращаемое значение давайте вернём какую-нибудь строку ну и видим да что эти функции одинаковые ну и также у нас там есть один аргумент number давайте его добавим если мы при этом попытаемся добавить ещё какой-то второй аргумент то Typeescriptрипt сразу начнёт ругаться потому что ожидается только один аргумент на вход ну вот да он пишет собственно говоря про это ну если здесь его добавить тоже всё будет хорошо ну если ещё попытаться заменить на стринг например ну естественно ошибка тоже будет просто убеждаемся что с сигнатурой всё совпадает так здесь вроде бы про всё рассказал надеюсь ни про что не забыл но тема в принципе не самая важная давайте двигаться дальше 2 часа материала позади и впереди ещё достаточное количество контента поэтому давайте подведём промежуточные итоги как мы выяснили Typeesриpt - это язык со статической типизацией при этом она явная или выводимая и что очень важно структурная когда вы поймёте значение всех терминов в этом выражении вам писать код станет действительно проще следующая важная мысль которую я тоже много раз уже раскрывал на протяжении этого урока - это относись к типам как ко множествам здесь действительно многие концепции станут понятны и вы поймёте как они между собой пересекаются взаимодействуют и как с ними работать следующий итог - это скорее предостережение для начинающих старайся в продакшн-коде не использовать те самые any ts ignore type assertion non operator о которых мы говорили на этапе обучения в педпроектах это ок если ты сталкиваешься с какими-то трудностями но в реальных проектах держись от них подальше ну и важно понимать что акцент сейчас стоит сделать на базе в какие-то детали прямо не углубляться по типу того чем тип отличается от интерфейса если таким мусором сейчас будешь голову забивать далеко ты не уедешь а свой базу пиши код потихоньку остальные какие-то детали будешь уже получать из практики к подобному видео ты всегда можешь вернуться и какие-то детали подглядеть посмотреть заново освоить понять ну а мы двигаемся дальше и следующая тема уже такая из разряда продвинутых и называется она Mapet Types переходим сразу к примерам но сначала небольшая терминология map types - это типы которые позволяют создавать новые типы на основе существующих изменяя какие-то поля представим что у нас есть юзер и мы хотим сделать новый тип с такими же полями но все поля у него должны быть необязательные или например readдоли поля или какие-то типы мы заменить хотим или ещё что-то в общем любые махинации которые нам требуются позволяют выполнить Map types давайте посмотрим как с этим всем работать начнём с базового такого примерчика который всегда разбирают это как разли или optional поля давайте начнём с Redonли реализуем такой тип заворачивая в который любой другой тип мы сделаем все его поля здесь естественно нам работать понадобится с Gририicком и синтаксис здесь следующий мы вот в таких квадратных скобках указываем с чем мы хотим работать в данном случае уже с помощью известного KOF мы из Genericка достаём как раз ключи ну здесь логично и потом по этим ключам мы добавляем значение ну здесь вот этот K можно заменить на любое другое название можем K использовать какой-нибудь просто более краткую запись так ну ещё раз давайте эту запись проанализируем мы добавили Generic вытащили ключи из этого Genericка с помощью KOF потом по этому ключу получили значение у этого дженерика через квадратные скобочки то есть на первый взгляд запись достаточно такая запутанная и сложная но если уметь её читать то ничего сложного здесь нет всё достаточно просто ну давайте попробуем создать нового пользователя который как раз будет с Redonли полями то есть по сути мы просто вот в этот read type оборачиваем видим что все поля у нас скопировались но при этом мы их ещё не сделали и здесь ещё смотрите важный нюанс когда вы попытаетесь в такой map types добавить ещё какое-то поле Typeesриpt вам это сделать не даст там в принципе по сообщению об ошибке понятно что не так в такой тип просто нельзя что-то добавлять ни поля ни методы вот но для того чтобы эти поля сделать доли мы просто добавляем и видим что они не просто скопировались а стали все редонли например мы хотим их сделать опциональными ещё тоже здесь никакой проблемы нет добавляем вопросительный знак видим что они стали у нас ещё и опциональными хотим сделать чтобы они ещё и nullable были то есть могли принимать значение nл опять же также никаких проблем нет добавляем Union и видим что у нас также эти значения все могут принимать теперь на при этом здесь ещё такой механизм интересный есть давайте вот этот тип оставим как есть сделаем новый поменяем у него название пусть будет edit type а здесь давайте сделаем optional type вот и смотрите например мы хотим сделать чтобы у нас поля наоборот из опциональных стали обязательными или издонли стали изменяемыми нам достаточно просто вот так вот минус добавить и по сути мы как бы отменяем действие этого редонли действие вопросительного знака опциональность делаем такую цепочку сначала все поля делаем донли потом этот редоли исключаем но и видим что у нас поля вновь стали изменяемыми то есть как видите здесь достаточно гибкий тоже такой механизм можно изменять объекты так как вам это нужно давайте ещё на какие-нибудь примерчики посмотрим сделаем тип назовём его аналог по факту это не будет являться аналогом массива но просто чтобы вы суть понимали мы можем поля сделать например просто набором чисел K in number пишем вот такую вот конструкцию и дальше указываем тип который будет ну и давайте попробуем сделать например массив строк аналог generнериком указываем string ну и передаём в этот массив стринги добавляем строки всё хорошо пробуем добавить сюда како число ну получаем естественно ошибку в качестве ключа сейчас у нас используются числа можем заменить и использовать стринги то есть по факту у нас будет объект здесь мы указываем поля строковые но если попробуем добавить какую-нибудь пятёрку то Typeesкрит будет ругаться в обычном объекте вы сможете так указать в общем этими примерами я хотел показать что мы можем не только ключи из какого-то объекта доставать но и задавать набор чисел набор строк здесь в принципе всё зависит от задачи которую вы решаете поле для скажем так действий достаточно большое так давайте двигаться дальше рассмотрим сейчас уже такие более продвинутые инструменты представим что у нас есть несколько типов в данном случае это usеer car и какой-то рандомный тип и у всех у них есть общее поле type представим ситуацию что вы хотите написать такой тип который это поле type будет исключать для этого есть и другие механизмы более простые сейчас я вам показываю именно особенность работы с вот с Maped Types и какие там есть возможности давайте вот такую вот базовую конструкцию оставим когда мы просто копируем поля объекта и здесь мы можем воспользоваться Type assertion вот таким вот с помощью а скастовать что-то к чему-то сейчас мы воспользуемся утилитарным типом с которым вы ещё мне знакомы но относитесь к нему как к инструменту который позволяет исключить из типа какие-то значения в данном случае мы хотим исключить поле type из нашего набора ключей то есть мы взяли вот этот К сказали: "Преобразуй его к типу у которого за exclusion исключён тип type конструкция сложная поставьте на паузу если что вникнете" но давайте посмотрим как это сработает используем Visout Type берём user и видим что поле type было исключено опять же мы могли здесь вот эту всю конструкцию without Type не писать и воспользоваться сразу exclude или там каким-то другим утилитарным типом но здесь я вам показываю именно суть что мы можем вот в этом Mapet types использовать вот такие вот конструкции преобразовывать их так как нам нужно например можем воспользоваться даже шаблонными литералами и не просто взять ключи а как-то их ещё и изменить в данном случае добавить перед названием ключа get то есть сделать геттеры для ключей здесь я конструкцию скопировал мы ещё используем capitalizйize чтобы следующую букву после get сделать заглавной опять же повторюсь это механизмы уже такие более продвинутые ну и как видите Typeesриpt из изначального нашего типа car или давайте тип usеer возьмём сделал тип у которого не просто набор полей а он к ним ещё добавил get и первую букву поля сделал заглавной то есть механизм супергибкий использовать его можно вот только как вы хотите сейчас мы например убрали переписку get и сделали просто чтобы каждое поле у нас было с заглавной буквы это просто учебные примеры понятное дело что именно такой кейс вам в работе скорее всего не пригодится но вы видите возможности для лучшего понимания давайте ещё сейчас такую махинацию проделаем открою среду разработки у нас здесь установлен Typeescript зайдём в файлик с самой библиотекой поиском здесь напишем exclude и вот он этот элю который мы как раз использовали но если мы здесь пролистаем чуть выше мы увидим здесь много базовых стандартных классов которые как раз делают объекты редоли поля обязательными поля опциональными как раз то есть это вот эти мапы types которые вынесены просто в утилитарные типы которые вы быстро можете использовать и не писать их самостоятельно от проекта к проекту exclude кстати работает вот таким вот образом можете тоже поставить на паузу и вникнуть чуть позже мы ещё про все эти типы в разделе про уity types поговорим ну то есть здесь вот как раз базовые знания,ки условные типы использование never как видите оно здесь повсеместно практически используется тут вот этих утилитарных типов достаточно много и сейчас мы про них как раз будем говорить ну и на этом Smap Types мы заканчиваем и переходим как раз к утилитарным типам прямо таким явным пересказом документации честно говоря не очень хочу заниматься потому что утилитарных типов этих тут достаточно много я по сути просто информацию буду дублировать поэтому самое адекватное решение как мне кажется сейчас - это пройтись по самым основным и если там есть какие-то нюансы отдельно их уже разобрать а остальное в документации вы уже сами почитаете и посмотрите на примерчике итак первый тип aed используется вообще не часто однако иногда бывает нужен он умеет рекурсивно разворачивать промисы и доставать то что там внутри лежит грубо говоря это тот же AT который вы используете в JS для промисов только для типов partial его аналог мы уже писали это map Types который для типа делает все поля опциональными похоже мы вот прямо один в один уже писали вот здесь даже можно посмотреть как он выглядит делает поля возможными устанавливать их в undefined required полная противоположность делает поля обязательными redonly думаю здесь из названия понятно делает поля Redonly точно такой же тип мы уже писали в предыдущем разделе вот recкоord конструкция уже поинтересней её мы разберём чуть позже отдельно пока давайте пройдёмся по другим утилитарным типам пик тоже штука интересная давайте смотреть прямо на примере здесь думаю лучше разобрать сейчас у нас есть тип usзееer мы хотим создать новый тип на основе этого юзера но при этом мы не хотим здесь использовать объединение какое-то наследование расширение мы просто хотим вытащить несколько полей из него и вот пик позволяет точечно эти поля цеплять первым дженериком мы передаём тип с которого мы хотим вытащить поля вторым дженериком вот таким вот юнионом передаём какие поля мы хотим выцепить ну как видим у нас появился новый объект в который подтянулось поле name и поле friends в паре рядом с ним всегда идёт похожий инструмент но который поля исключает то есть видим что здесь у нас подтянулось как раз наоборот всё кроме name и кроме friends если мы хотим получить тот же результат что и в первом случае то мы как раз наоборот исключаем поля H и type и у нас остаются name и friends ну механизм здесь простой думаю вам должно быть всё понятно то есть в первом случае мы поля какие-то цепляем во втором случае мы поля наоборот исключаем тут из названия самих типов понятно частенько бывает необходимость выцепить какое-то одно значение например из какого-то типа в котором объявлен цвет мы хотим вот этот цвет вынести в какой-то отдельный тип но здесь важно помнить что всегда можно воспользоваться более простой конструкцией и вот так вот грубо говоря индексным доступом это поле точно также вытащить просто через квадратные скобки ну то есть пик в данном случае имеет больше смысла когда у нас несколько полей а мит ещё раз повторюсь когда нам какие-то поля необходимо исключить двигаемся дальше следующие два типа - это exclude и мы уже использовали но так пока что неосознанно непонятно давайте смотреть как они работают в целом это очень похожи и на пик и на онструменты но работают они с Unionпами создадим Union type color ну пусть в нём будет три значения: Red green blue ну и попробуем сразу воспользоваться одним из утилитарных типов попробуем получить какой-то более узкий набор цветов воспользуемся exclude и из этого набора исключим например голубой цвет ну и то же самое давайте сделаем с экстраct и посмотрим какой результат у нас получится в целом механизм работы тут точно такой же как и у Пик и ОД давайте единственное ещё для большей наглядности ещё один цвет добавим и здесь исключим два цвета а здесь наоборот вытащим два цвета так единственное вот здесь да я заметил я ошибся не то заменил здесь делаем экстракт color ну да видим что здесь у нас результат redгen здесь у нас должен быть ровно противоположный результат здесь у нас blue и yellow то есть то что мы вытащили то и получили здесь в принципе тоже никаких сложностей в понимании быть не должно всё то же самое только работаем с юнионами так двигаемся дальше уже на протяжении этого ролика мы затрагивали два утилитарных типа это Return Type и Parameters очень полезные типы которые достаточно часто используются и про них важно помнить потому что без них иногда бывает тяжко можно немного подзависнуть здесь тоже всё достаточно легко ну давайте посмотрим как этим пользоваться мы уже рассматривали где-то в начале видео но давайте посмотрим ещё раз хотим получить тип который возвращает функция в данном случае это string используем утилитарный тип return type ну и в качестве generрика мы передаём саму функцию давайте внутрь этого return typйпа провалимся посмотрим что там внутри конструкция там достаточно сложная но нас интересует generic как видите здесь стоит constraint ограничение на то что мы можем принять функцию с любым количеством аргументов и с любым возвращаемым значением то что идёт дальше пока что не обращайте внимания это мы тоже будем разбирать ключевое слово inf соответственно из этой функции нам естественно надо вытащить тип поэтому используем typйпов ну и как видим результат который у нас получился равняется тому который мы указали в самой функции частый кейс - это когда у нас внутри библиотеки есть какая-то функция там не знаю React render например рандомный просто пример и мы из него хотим тип получить возвращаем его значение ну вот в таком случае мы return type используем если вы в коде какую-то функцию сами написали скорее всего вы тип и так можете отдельно куда-нибудь вынести и его использовать так return type думаю здесь понятно используем его когда хотим получить тип возвращаемого значения из функции если мы хотим получить тип аргументов используем parameters механизм здесь абсолютно такой же используем параameters и в качестве generic передаём функцию ну видим да что нам возвращается здесь массив если бы у нас было несколько аргументов соответственно это был бы массив из нескольких аргументов с несколькими элементами внутри так здесь думаю всё тоже понятно давайте двигаться дальше что здесь ещё из такого часто используемого ну здесь вот есть всякий аerкей lverкейс для того чтобы работать с стринговыми типами там полностью шрифт весь сделать заглавными полностью строчными первую букву мм сделать заглавной сделать срочной ну иногда бывает полезно но используется не прямо супер часто так ну из того что используется ещё часто здесь мы не разобрали рекорд вот эту штуку стоит разобрать отдельно она для работы с объектами давайте здесь вернём примерчик где у нас были цвета заранее заданные вот это вот всё лишнее уберём представим себе ситуацию у нас вот есть набор каких-то литералов например цветов и мы хотим получить на выходе объект у которого вот эти литералы будут использоваться в качестве ключей а в качестве значений может быть абсолютно всё что угодно: массивы объекты строки в общем всё что вам нужно в таком случае мы используем recкоord если мы заглянем внутрь у него есть два дженерика первый - это как раз ключи а второй - значения которые по этим ключам в объект будут добавляться причём важно что вот в базовом вот таком вот использовании Tapeescриpt будет запрашивать у вас заполнение всех ключей как видите сейчас он просит чтобы для каждого цвета вы указали какие-то значения в данном случае это массив стрингов чтобы все четыре сейчас не заполнять давайте лишни уберём оставим грин для него тоже добавим массивчик bl заменим на Red ну и видим что все ошибки пропали в данном случае рекорд у нас заполнен правильно если же мы вдруг хотим чтобы у нас ключи были необязательными но при этом соответствовали сигнатуре такой которую мы указали то есть ключи у нас должны быть цветами мы должны использовать э тип который сделает просто все поля опциональными в данном случае это тип partial опять же тут примеров использования достаточно много это могут быть какие-нибудь статусы для которых вы что-то заполняете либо же наоборот вы можете скомбинировать например статус и цвет то есть для статуса success у вас greгen цвет для статуса eror у вас там red цвет и так далее ну то есть рекорд позволяет объявить объект у которого определён набор ключей с заданными значениями на этом с утилитарными типами мы заканчиваем у начинающих часто когда они видят все эти рекорды паршалы и они пугаются от того что кот становится страшный но на самом деле если один раз вот это разобрать тема супер простая всё прямо интуитивно понятно углубляться здесь даже особо и не во что итак двигаемся дальше немного откатимся назад чуть раньше мы рассматривали тайпгурды где мы с помощью вот такой кастомной функции можем сузить тип и проверить что он является тем или иным ну типом тут тавтология ну и также мы рассматривали механизм преобразования типов type assertion когда мы вот таким вот образом явно кастуем один тип к другому есть ещё один механизм похожий на Type Guardard который называется ASERST давайте посмотрим на первый пример здесь два вообще подхода к тому как с этим работать как видите здесь объявляется также функция указывается аргумент но в качестве возвращаемого типа указывается вот такая конструкция asserts value отличие ASTs в том что здесь в случае если тип не совпадает пробрасывается именно исключение выбрасывается ошибка вот такая вот запись она гарантирует что полученное value которое мы изначально не знаем какого типа у нас не пустое то есть оно не равняется n и не равняется undefined ну и по сути если вы используете вот такую вот функцию внутри кода Tapescript сразу понимает что n undefined во всём последующем коде у вас отсутствует то есть у вас точно есть значение в данном случае вот как видно на этом скрине это значение string и мы можем с ним полноценно уже работать и второй способ - это asserts value is type выглядит он вот таким вот образом точно также объявляем функцию но в отличие от левой записи где мы просто делаем assert value здесь мы утверждаем ещё что это value относится к какому-то типу здесь точно так же как и в Тайпгарде мы делаем любые проверки тайпов проверяем какие-то поля существуют не существуют делаем ну абсолютно любые проверки которые нам нужны чтобы доказать что это value является нужным для нас типом но отличие от обычного тайпгарда где мы делаем просто values type здесь у нас также пробрасывается исключение то есть здесь мы прям строго проверяем что value соответствует в обратном случае пробрасываем ошибку это уже такая самая мощная форма которая делает проверку и утверждение типа своего рода такой валидатор в котором мы уже явно проверяем при и при несоответствии пробрасываем ошибку ну и как обычно давайте сразу смотреть на пример здесь я в принципе перенёс то что было на слайдах давайте начнём с того что попроще где мы просто проверяем на наличие того или иного значения что оно не равняется на или undefined ну как я уже сказал здесь ключевой момент что пробрасывается именно ошибка и эту ошибку мы в дальнейшем можем как-то обработать залогировать отправить метрику выдать какой-то алерт пользователю в общем это своего рода такой уже получается валидатор если мы запустим функцию с каким-то значением отличным отлиfined ну естественно она пройдёт нормально ну и как я уже сказал тут основное отличие от обычного тапгарда только не то что проверяется тип на соответствие тем правилам которые мы описали внутри тайпгарда здесь ещё пробрасывается ошибка которую мы можем обработать соответственно мы можем этот осерт обернуть в trйк catch мы можем прямо линтер написать который на функции содержащий в себе асерт будут требовать оборачивать их трайкч ну и соответственно все случаи в которых у нас этот осерт по какой-то причине не прошёл хотя мы ожидаем что у нас там будет то или иное значение ну эту ошибку как-то можем логировать там не знаю опять же метрику какую-то куда-то отправлять куда-нибудь что-нибудь записывать как-то это анализировать потом смотреть как часто такие ошибки стреляют почему у нас данные не соответствуют тем которые мы ожидаем и так далее то есть ну основной смысл здесь именно в этом такие функции удобно использовать например когда мы получаем данные из БКА с помощью какого-нибудь и нам нужно их провалидировать если ну конечно эта валидация требуется у нас по задаче мы данные провалидировали подогнали их под наш тип который мы действительно ожидаем убедились что всё окна работа с этими данными так с этим думаю понятно давайте рассмотрим второй пример где у нас идёт проверка уже на какой-то конкретный тип в данном случае на соответствие user внутри видим что у нас идёт проверка на object и проверка на n now и затем мы проверяем что у нас поля соответственно name и соответствуют string и number то есть ну здесь такие базовые проверки давайте сразу рассмотрим примерчик создадим объект который у нас не помечен как usер но у него содержится поле name ну и сделаем функцию которая как-то этого юзера обрабатывает на вход она ожидает объект с типом usеer ну и допустим просто вводит в логи здесь не столь важно давайте эту функцию вызовем и передадим туда объект object который мы объявили выше естественно видим ошибку потому что поле у нас отсутствует в этом объекте а поле это всё-таки является обязательным теперь мы можем дёрнуть вот эту функцию assert user передать туда этот object и вызвать функцию prepare user ещё раз ну и как видите ошибка пропала то есть в данном случае Tpescript уже точно понимает что этот объект у нас соответствует юзеру иначе эта функция пробросит исключение и до следующей строчки дело просто не дойдёт понятное дело что если сейчас мы запустим мы увидим что Property H у нас не объявлено ошибка есть но однако на уровне типов у нас всё сходится и мы можем писать более безопасный код теперь представим что это вот антипаттерн так делать естественно не стоит давайте здесь заменим на log user а в prepare user мы как раз будем добавлять поле просто рандомно его проинициализируем опять же повторюсь так делать не надо мутировать константные объекты не надо ну и чтобы у нас Typeesриpt не ругался на тридцать четвёртой строчке воспользуемся ещё одним антипаттерном скастуем Object к usзееру ну такое начинающие в коде делают поэтому такие ошибки даже в реальном продакшн коде возможны но в любом случае вот этот prepare usеer у нас изменит object так что он будет соответствовать типу usеer и ошибки у нас не произойдёт как видите вот этот асерт отработал корректно ошибка не пробросилась и lкюзеer вполне нормально отработал ну и как я уже сказал в идеале ещё и ошибочку обработать в тйч это всё завернуть там сделать как надо ну и всё тоже в принципе инструмент вполне рабочий не так часто используются как обычные тайпгарды но тоже знать что такое есть надо иногда вы будете в коде такое встречать следующий механизм про который поговорим - это перегрузка функций активно используется в типизированных строго типизированных языках программирования Java C#ARP и так далее ну и давайте смотреть на пример представим что у нас есть функция которая как-то обрабатывает дату не очень интересует нас как она это делает но делать это она умеет в первом случае она принимает таймстп во втором случае она принимает месяц день и год в третьем случае такой более универсальный вариант в котором она может принимать месяц или таймстмп ну и также опционально день и год и соответственно внутри мы в зависимости от каких-то проверок как-то эти данные обрабатываем вот это есть перегрузка функции когда мы для одной реализации так скажем для одной функции объявляем несколько сигнатур с разными входными параметрами с разными значениями на выходе но реализацией одной и в конце как видите мы эти различные реализации можем использовать то есть Typeesриpt у нас сейчас видит что у нас возможно два варианта либо когда один аргумент timeст либо когда три сразу вот и соответственно два аргумента он уже не принимает ну то есть ещё раз давайте подытожим перегрузка функции - это способ когда мы можем для одной реализации объявить несколько сигнатур зависимости от каких-то условий внутри самой реализации как-то по-разному с ними работать при этом как я уже сказал здесь может отличаться не только количество аргументов и их типы но и возвращаемое значение при этом здесь тоже важно быть разумным вот например вот такой вариант он не имеет никакого смысла то есть у нас видите здесь три сигнатуры описано в каждом добавляется новый аргумент по сути мы могли два вот этих последних аргумента сделать просто необязательными и смысл был бы ровно такой же то есть смысла от такой перегрузки абсолютно нет следующий бессмысленный пример тоже похожий представим что у нас вот есть функция мы написали две перегрузки одна принимает strтринг другая принимает number такое в принципе тоже в коде можно встретить при этом возвращаемое значение как видите у них одинаковое и там и там возвращается numberр при этом здесь тоже можно было это решить совсем по-другому просто объявить функцию в которой аргумент может бытьстн или numberр то есть перегрузка здесь никакая не нужна в общем здесь тоже как и везде надо просто со здравым смыслом подходить и правильно использовать инструмент ну а мы двигаемся дальше ну а мы двигаемся дальше наверное одна из таких самых сложных тем в тайскрипте которая даётся тяжело и даже не все опытные ребята понимают как с этим работать это условные типы и так называемый infфер с условными типами в принципе мы уже отчасти познакомились чаще там просто сама конструкция может выглядеть громозская непонятно но если уметь её читать то там на самом деле всё интуитивно понятно и достаточно легко давайте ещё раз вспомним закрепим то есть условный тип - это когда мы вот так вот через extends пишем такой аля тернарный оператор и в случае если у нас тип соответствует тому с которым мы его проверяем мы возвращаем один тип в обратном случае возвращаем другой тип то есть ну классическая условная конструкция но это я вспоминаю когда 5-6 лет назад я пытался сам инфер изучить я не особо по тем объяснениям которые были понимал что он из себя представляет на самом деле тоже ничего сложного нет давайте вот представим что у нас есть функция у неё есть аргумент у неё есть возвращаемое значение как нам написать такой условный тип в котором мы сможем работать с аргументом этой функции или же с возвращаемым значением ответ использовать утилитарные типы return type и параметр здесь не принимается потому что сразу возникает вопрос: а как они это делают как они достают эти типы давайте разбираться смотрим на пример итак у нас есть функция в которой есть пару аргументов какое-то возвращаемое значение в принципе всё по классике теперь давайте попробуем написать свой аналог утилитарного типа parameters который будет вытаскивать как раз вот эти аргументы из этой функции типа этих аргументов ну конечно же нам понадобится здесь generic также нам важно проверить что в качестве этого дженерика действительно передана функция ну и мы делаем условие если т у нас расширяет ну то есть является функцией то мы будем возвращать один тип то есть нам теперь каким-то образом надо вытащить параметры и вернуть их в обратном случае будем never возвращать потому что нам передали не функцию мы не можем вытащить у неё аргументы ну и теперь каким-то образом нам вот из этой функции которую мы приняли дженериком необходимо эти параметры вытащить для начала каким-то образом нам эти параметры необходимо объявить просто указать там string number мы уже умеем проблемы нет но как объявить какой-то вот динамический тип который мы сможем вот здесь вот использовать вот просто взять его вытащить и вернуть и вот здесь как раз на помощь приходит infр который позволяет это делать то есть по сути тип вот этот вот с помощью infр мы выводим вытаскиваем из нужного для нас места и здесь как с дженериком мы можем дать ему любое название будь то arc type как угодно и потом в дальнейшем его использовать вот таким вот образом то есть конструкция как видите на первый взгляд вот если вы на неё смотрите она сложная extends inf какое-то условие never но если разобрать по полочкам то читается она прямо дословно если вы передали в качестве Genericка функцию достань из неё аргумент в обратном случае просто верни пустое множество верни never пробуем получившийся тип использовать смотрим на Fn Arc и видим never всё дело в том что у нас сейчас вот это условие txtense не обрабатывает потому что функции которую мы указали у нас ожидается один аргумент а здесь у нас их на самом деле два давайте уберём оставим один и видим что он вывел этот тип мы его смогли получить видим что это стринг на самом деле тут чуть-чуть переписать для того чтобы это работало для любого количества аргументов давайте второй вернём когда мы описываем вот тут сигнатуру функции понятное дело мы можем здесь и два аргумента указать то есть сделать ещё один инфер уже с другим каким-то обозначением пусть это будет inf а и вернуть мы можем уже да вот здесь видите у нас strтринг возвращается а здесь мы можем вернуть не просто один аргумент а можем вернуть массив этих аргументов ну это такое не универсальное значение понятное дело но этим я хочу показать что мы здесь а можем делать всё что захотим вот как нашей душе угодно так мы и можем это всё наворотить ну вопрос только какой в этом смысл ну понятное дело что если мы хотим какое-то универсальное решение написать здесь необходимо рестпараметрами воспользоваться вот таким вот образом вывести их уже в отдельный массив с помощью вот оператора строеточием rest parameters и вот таким вот образом вернуть сейчас мы можем работать с любым количеством аргументом как видите сейчас он их в массиве вполне спокойно вывел и сколько бы мы сейчас новых не добавили они будут работать при этом если мы зайдём в саму спецификацию посмотрим как здесь работает у нас parameters давайте поиском его найдём то мы увидим здесь абсолютно такую же конструкцию ну единственное вот здесь они ещё ограничение constraint добавили в объявление самого дженерика ну это если прямо до идеала доводить да естественно это ограничение нужно делать чтобы Typeesриpt понимал что мы именно функцию здесь ожидаем ну а здесь видим да что сама вот эта правая часть всего этого выражения она ровно такая же вытащили аргументы вернули их если передали не функцию вернули never давайте теперь попробуем написать точно такой же тип но уже для return type механизм здесь будет абсолютно такой же также в extens указываем функцию но здесь уже аргументы нас не столько интересуют их можно указать какним здесь нас интересует именно то что возвращает эта функция то есть видите сверху мы указывали потому что нас не интересовало там возвращаемое значение а здесь мы будем работать уже как раз именно с ним вот ну и наша задача - вытащить этот тип и вернуть его вот в этом условии ну и здесь точно так же используем inf указываем название и возвращаем ну то есть вот опять же если смотреть на готовый результат выглядит сложно если прямо по кусочкам начать разбирать это выражение оно супер простое и как работает infр прямо становится очень очевидно и очень понятно ничего трудного в этом нет и бояться не нужно ну как видите он действительно выводит нужное значение которое функция возвращает но работать с инфером можно не только в функциях не только в аргументах не только с ретрйпом вы извлекать таким образом можете абсолютно всё что угодно например можете извлечь элемент из массива какой-то тип элемента массива давайте создадим такой тип назовём его getray item здесь у нас будет ограничение на массив как раз поэтому делаем extendse any давайте пока без базовую конструкцию напишем в случае если у нас условие не выполняется возвращаем never здесь не забываем что у нас массив ну и да теперь нам необходимо как-то вот этот элемент массива вытащить и вернуть его делаем всё то же самое здесь единственное нам понадобится в скобочке обернуть чтобы потом указать что это массив делаем infer type то есть вот точно так же можем U назвать его можем А назвать можем B назвать можем какое-то осознанное название дать как типа item type и вернуть его давайте сразу же попробуем этим воспользоваться создадим массив стрингов добавим что-нибудь у него ну даже можно в принципе не добавлять ну теперь попробуем извлечь тип используем наш получившийся get ray item type туда передаём наш массив но от него естественно тайпов надо взять ну и смотрим видим что тип действительно извлёкся если бы у нас были здесь numberры он бы вытащил вот этот numberр как раз давайте на утилитарные типы ещё взглянем здесь есть вот такой уйдт который промисы раскрывает и здесь вообще тип выглядит очень сложно но если мы заглянем внутрь мы увидим здесь целых четыре инфера и вот так вот последовательно рекурсивно он раскрывает промисы доставая то что находится внутри тоже можно вот ради интереса можно даже пройтись поиском по этому файлу везде посмотреть как используется inf и вы увидите что конструкция ну в целом несложная несложная она читается тяжело для начинающих это вообще взрыв мозга но на самом деле всё просто все вот эти вот страхи пользоваться этим они в голове всего лишь сидят на самом деле ничего сложного здесь нет и на этом друзья с тайпскриптовыми темами мы заканчиваем сейчас посмотрим как конфигурировать проект как настроить tapйприpt и начать на нём разрабатывать уже реальные проекты а затем я покажу вам пример хорошего проекта на Реакте на Тайpeскрипте большого правильного наполненного насыщенного тоже будет плюсиком итак естественно у вас должен быть установлен NOD JSNPM сейчас у нас пустой проект ну и в первую очередь проверим что npm у нас установлен для этого пишем npm или note - V видим что npm у нас установлен далее необходимо проект проинициализировать чтобы мы уже какие-то пакеты могли устанавливать для этого пишем npmниit с флагом Y после чего у вас будет создан package ну и теперь очевидно необходимо установить сам Tapeesриpt устанавливаем его как деф-зависимость так как в runtтай он не попадает он используется только на этапе разработки именно для компиляции кода далее нам необходимо проконфигурировать сам TapeScript это можно сделать вручную создав файлик TSCig либо можно также проинициализировать по дефолту npx TSC минус Enit tsc - это сокращённо Typeescript компиilator там скорее всего у вас тоже попросит его установить но если что согласитесь он установится как видите в корне проекта у нас появился также файлик T config gson давайте сейчас всёвсёвсё лишнее поудаляем тут куча всяких настроек с описанием на досуге можно почитать что он умеет но сейчас в таком количестве они нам не нужны итак оставим самые-самые базовые которые по дефолту не закомментированы итак таргет - это версия джаваскрипта в которую по итогу мы будем компилировать здесь их много можно скомпилировать в самые свежие можно скомпилировать в S6 можно во что-то более старое зависит от того что вы хотите поддерживать давайте здесь например 2016 укажем дальше указывается модульная система это то как вы будете работать с импортами экспортами через require или через привычный импорт-экспорт и так далее вот стрикт-режим настройка которую нужно ставить обязательно всегда в true иначе эффективность тейпскрипта теряется в разы это строгий режим который будет как раз все такие спорные ошибки подсвечивать и это очень важно так вот здесь действительно на каждой прямо этой опции останавливаться смысла нет вот это точно можно посмотреть в документации ну а сейчас давайте попробуем создать какой-нибудь файлик и написать здесь Typeescript код и скомпилировать его давайте объявим какой-нибудь тип уже привычный какой-нибудь typeuser нам тут среда разработки сразу подсказывает чем его можно заполнить сразу же из этого типа создадим объект укажем что у нас это объект типа usеer ну и для того чтобы теперь скомпилировать этот файлик мы используем тот же самый TSC Tescript компилятор и указываем путь до файла видим что у нас рядышком сразу создаётся JavaScript код JavaScript файлик в котором всё лишнее всё связанное с типами отбрасывается и остаётся чисто JavaScript но скорее всего вот так вот с голым тайпскриптом вы работать не будете даже если вы будете писать на Node jz скорее всего у вас там какой-нибудь сборщик будет или что-нибудь такое ну а на фронте если будет какой-то фреймворк по типу там view или тот же React по-любому вы будете использовать либо вид либо VPAC фаческий сборки поэтому давайте смотреть более реальный пример давайте поработаем с вид для этого нам необходимо будет проект проинициализировать для этого используем вот такую команду которую берём из документации если вы смотрите через пару лет возможно что-то поменялось поэтому заходим в документацию и смотрим как проинициализировать проект заполняем здесь базовые настройки указываем project name в данном случае укажу точку чтобы выбралась текущая директория нажму игнорировать файлы и продолжить дальше он просит выбрать фреймwork ну поскольку у нас тут всё вокруг Реакта крутится давайте выберем его обязательно Tape scriptриpt и видим что проект у нас проинициализировался здесь все файлы с исходным кодом как видите у нас на тейпскрипте при этом где используется JSX реактовская разметка там мы используем TSX расширение не просто TS а TSX расширение здесь вот сходу можем увидеть оператор NOН Nal который мы разбирали который я не рекомендовал использовать но кстати вот тут у вас он оправдан потому что без рутовского элемента у вас в принципе приложение сам React не сможет монтировать компоненты давайте npm install ещё сделаем чтобы у нас установились все пакеты в NOD modules ну и посмотрим что у нас здесь вообще от тейпскрипта есть давайте попробуем типизировать какой-нибудь state по умолчанию он сам может вывести значение которое мы передаём в качестве state но также мы можем явно указать его и в generнерике видим что сразу скриpt ругается что ожидается numberра мы передали строку если заменим numberр на на стринг естественно ошибка пропадёт ну поскольку здесь у нас счётчик оставляем нолик number и поехали дальше что здесь с конфигурацией как видим здесь у нас есть три файла мы их можем декомпозировать давайте посмотрим на то как это работает есть общий тест-конфиг который в референсах ссылается на два других конфига в этих конфигах заданы какие-то базовые настройки при этом у вас может быть такой же тест-конфиг отдельно для тестов отдельно там для сборки не знаю там ещё отдельно для деф среды отдельно для продакшн среды ну в общем ну хотя последнее - это отчасти антипаттерн лучше так не делать но в общем здесь опять же можно декомпозировать так как вам нужно можно сделать какой-нибудь общий базовый тест-конфиг который будет для всего его переиспользовать в тест-конфиге для тестов в тест-конфиге там я не знаю для сборки для not JS для фронт-кода в общем здесь тоже важно помнить что такое есть и просто умело этим пользоваться в целом друзья считаю что тут самое главное показать в целом как развернуть проект как его запустить а то как настраивается тесткоfig но это уже детали про каждую опцию вполне можно прочитать там пару строчек в документации и разобраться с этим уже самостоятельно главное было от меня в этом видео донести суть самого Тайпскрипта ну и перед тем как посмотреть на пример топового проекта на Typpeesриpt давайте им дам несколько советов для начинающих первое ну и самое наверное важно не зацикливаяся на сложных типах на всяких условных конструкциях мапы types инфиринге начни с малого освобопиши проекты и дальше уже потихоньку по мере необходимости будешь доучивать то что тебе необходимо какие-то уже такие узкие специализированные кейсы как я уже сказал лучшая тренировка - это петпроекты напиши какую-нибудь социальную сеть интернет-магазин вот решай проблемы по мере их поступления хоть я и говорил на протяжении урока что иignor type assertion - это зло в продакшн-коде да действительно это зло использовать не стоит но всегда важно помнить что в петпроекте за это тебя никто ругать не будет главное понимай что ты это делаешь на этапе обучения что в продакшн-коде их использовать лучше не стоит но на этапе разработки вот на этапе обучения они сильно тебе могут помочь потому что ты действительно будешь сталкиваться с какими-то ситуациями где тебе сложно будет разобраться пробуй использовать чат GPT если вдруг всё равно закопался не смог не решил естественно заигнорил пошёл дальше потом когда-нибудь когда опыта наберёшься вернёшься и решишь проблему ну и последний пункт от части похож на второй пропет проекты только здесь хочется сделать акцент на абстрактных задачках часто люди закапываются в какие-то тренажёры решают какие-то задачки с помощью тейпскрипта там с помощью типов решают судоку а вопрос нафига если это вас никак не приближает к тому чтобы писать реальные прикладные проекты лучше реализуй парочку интернет-магазинов там какую-нибудь социальную сеть сделай аналог Ютуба или что-нибудь такое чем ты будешь там не знаю месяц сидеть решать задачи ну это бестолковое занятие в целом задачи тоже иногда полезно решать но это такой приятный бонус приятное дополнение там периодически раз там я не знаю в месяц раз в несколько недель но не на постоянке либо когда у тебя уже есть знания база вот тогда можно ну и напоследок глянем на такой хороший большой эталонный проект который мы разрабатывали на моём курсе Продвинутый фронт здесь всё начиная от каких-то конфигураций настройки тестов сторибука сборки вебпака написано на тайпскрипте прямо всё от а до я всё декомпозировано структурировано разложено по полочкам исходный код весь ну здесь конечно у нас fch slдиign используется всё разложено всё по красоте test config здесь достаточно простой давайте его откроем глянем здесь в принципе все такие базовые опции всё что надо настроено strictт Mode обязательно включен моду SNX чтобы использовать самые свежие импорты и экспорты даже здесь вот всё типизировано всё разложено всё по полочкам ну давайте куда-нибудь ещё заглянем посмотрим так здесь в принципе это для базовых каких-то штук мы там разбирали настроенные опишки ну сторибу здесь наверное тоже не особо интересно куча всяких самописных функций хелперов инструментов которые естественно все тоже строго типизированы какие-то вот обёртки поверх редаксовских каких-то там хуков селекторов писали для создания слайсов удобного тулзы так давайте ещё что-нибудь глянем что здесь есть ну естественно какие-нибудь страницы страницу например с набором статей здесь у нас отдельная бизнес-логика тоже вся типизирована ну давайте какой-нибудь компонент ещё откроем ну пусть будет фильтр с контейнер то есть по сути это такая обёртка которая данные стейта забирает и прокидывает их ниже уже в соответствующий виджет который компонует в себе какой-то поиск и несколько фичей так ну здесь наверное тоже так особо показывать нечего давайте ну вот какой-нибудро баoundary глянем например backтon - это тестовая кнопка у нас была ну тоже здесь вот классические компоненты такие про псы описали не про псы здесь конечно часами можно сидеть разбирать всё это ковырять вот есть такая глобальная декларация которая на весь проект грубо говоря действует и вы можете без импорта типа использовать там какой-нибудь departial optional record какие-то вот там импорты для SVG GPга давайте какие-нибудь ещё базовые UI компоненты глянем кнопки вот так у нас типизированы то есть всё задокументировано везде TS doc есть чтобы было понятно на Shad слое всегда рекомендую это делать модальные окна тоже все типы всё описано чтобы с этим удобно было работать вот так вот выглядят story book файлы ну и минутка саморекламы несмотря на то что курс уже 2 года или даже чуть больше он на 99,9% актуален за это время на фронте кардинально ничего не изменилось каких-то супермажорных обновлений не было технологии все свежие да какие-то библиотеки обновились какие-то там минорные изменения но в целом на курс на его качество это никак не влияет очень горжусь этим проектом до сих пор получаю от людей которые присоединялись позже или только заканчивают обучение комментарии такие вдохновляющие очень это радует в общем кто хочет присоединиться ссылочка будет в описании можете ознакомиться что там вообще есть и может быть вам этот курс понравится и подойдёт ну а мы на этом друзья заканчиваем вот такой вот ролик у нас получился на 2 12 часа надеюсь он будет для вас полезным понятным надеюсь я смог донести до вас какие-то базовые концепции не просто рассказал: "Вот это стринг вот это массив там это интерфейс это тип" а именно попытался донести какую-то более такую верхнеуровневую суть больше каких-то фундаментальных моментов ну и очень буду рад вашей поддержке все комментарии читаю лайки там подписки в общем по классике Ютуба ну и подписывайтесь на Telegram аккаунт там нет спама рекламы всё только по делу по существу буду рад каждому ну и увидимся в следующем ролике друзья всем спасибо