JavaScript Adept @javascriptadept Channel on Telegram

JavaScript Adept

@javascriptadept


Простыми словами про сложные вещи

https://www.youtube.com/channel/UCDghf3XJIeZWKlCgIfrhsHw

JavaScript Adept (Russian)

JavaScript Adept - это канал, который поможет вам понять сложные вещи в мире программирования на JavaScript. Username канала - @javascriptadept. Здесь вы найдете простые объяснения сложных тем, которые помогут вам освоить язык программирования JavaScript. Видеоматериалы, доступные на канале, помогут как начинающим, так и более опытным разработчикам улучшить свои навыки в программировании. Подписывайтесь на канал JavaScript Adept, чтобы быть в курсе последних трендов и новинок в мире JavaScript программирования. Посетите YouTube-канал по ссылке: https://www.youtube.com/channel/UCDghf3XJIeZWKlCgIfrhsHw и начните улучшать свои навыки прямо сейчас!

JavaScript Adept

25 Dec, 15:50


Advent Of TypeScript: Day 25 ⚡️

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

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

За неимением своих решений для обзора предлагаю в комментарии к этому посту накидать ваши варианты! Будет интересно увидеть разные подходы, особенно к тем задачам, которые я пропустил.

Итоговый вопрос: какие из 25 дней показались вам самыми сложными, а какие — наоборот, самыми интересными?

Спасибо всем за участие в этом марафоне! Пусть ваш код будет чистым, типизация строгой, а баги обходят стороной! 🎄

JavaScript Adept

23 Dec, 19:50


Advent Of Typescript: Day 23

Как и вчера, сегодняшнюю задачу все еще не решило сто человек. Выложу решения к концу адвента сразу скопом, либо в следующие дни. Для задачи 23 дня очень поможет реализация HotScript🥸

JavaScript Adept

22 Dec, 20:54


Advent Of Typescript: Day 22

Задачу сегодняшнего дня так и не не решило 100 человек, поэтому я не могу выложить решение. Впрочем, решать ее не обязательно, можно просто сделать проверки на тест-кейсы.. 🥸

JavaScript Adept

21 Dec, 17:55


Advent Of Typescript: Day 21

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

Решение в комментариях👇

JavaScript Adept

20 Dec, 11:40


Advent Of Typescript: Day 20

Сегодняшняя задача продолжает предыдущую - теперь нам нужно проанализировать JS код и узнать, какие переменные были объявлены, и какие были использованы. Нужно лишь немного изменить вчерашнее решение.

Решение в комментариях👇

JavaScript Adept

19 Dec, 12:34


Advent Of Typescript: Day 19

Задача 19 дня (кстати попадающая на мой день рождения) встречает нас с требованием создать небольшой парсер JavaScript кода, который бы на выходе отдавал код в виде узлов AST.

Решение в комментариях👇

JavaScript Adept

18 Dec, 10:19


Advent Of Typescript: Day 18

18 день встречает нас с простой задачей, решение которой было описано в недавнем ченжлоге TypeScript 5.4. Нам нужно типизировать один параметр функции на основе другого ее параметра.

Решение в комментариях👇

JavaScript Adept

17 Dec, 16:28


Advent Of Typescript: Day 17

В задаче 17 дня нам нужно затипизировать функцию compose, плюс еще несколько функций утилит.

Решение в комментариях👇

JavaScript Adept

16 Dec, 15:37


Advent Of Typescript: Day 16

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

Решение в комментариях👇

JavaScript Adept

15 Dec, 16:19


Advent Of Typescript: Day 15

Начинается хардкор, гораздо раньше, чем в предыдущем адвенте. Задача действительно объемная, без знания многих тонкостей ТСа ее не решить.

Для будущих дней прошу в 👉 чатик канала 👈, там можно обсуждать решения, да и в целом JS/TS.

А само решение в комментариях👇

JavaScript Adept

15 Dec, 15:24


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

JavaScript Adept

14 Dec, 10:00


Advent Of Typescript: Day 14

Сегодня в задаче предстоит узнать возвращаемый тип из асинхронного генератора, решение довольно простое.

Разбор в комментариях👇

JavaScript Adept

13 Dec, 13:05


Advent Of Typescript: Day 13

13 задача приветствует нас с теорией ко/контр/инвариантности. Решение довольно простое, однако давайте попытаемся понять, как оно работает.

Разбор в комментариях👇

JavaScript Adept

12 Dec, 13:39


Advent Of Typescript: Day 12

Сегодня нас ожидает задача с трансформацией огромного массива. Обычные методы обхода массива не подойдут - ловим ошибку по глубине...

Решение в комментариях👇

JavaScript Adept

11 Dec, 14:18


Advent Of Typescript: Day 11

В этой задачке нам нужно типизировать конструктор объекта. Задачи начинают усложняться!

Решение в комментариях 👇

JavaScript Adept

10 Dec, 09:30


Advent Of Typescript: Day 10

Сегодня нам нужно будет использовать побитовые операции (<< и |) в enum. Давайте сразу к решению, которое в комментариях 👇

JavaScript Adept

09 Dec, 08:56


Advent Of Typescript: Day 9

В девятой задаче мы продолжаем практиковать Module Augmentation - на этот раз нам нужно задекларировать модуль с несколькими типами внутри.

Решение в комментариях 👇

JavaScript Adept

08 Dec, 10:26


Advent Of Typescript: Day 8

Сегодня нас ожидает довольно хорошая, практическая задачка: нам нужно затипизировать process.env. Радует, что разработчики в этом году создают задачки опираясь на более реальные кейсы. И все же интересно, когда же уже будем писать калькулятор на TS...

Решение в комментариях 👇

JavaScript Adept

07 Dec, 10:40


Advent Of Typescript: Day 7

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

Решение в комментариях 👇

JavaScript Adept

06 Dec, 08:48


Advent Of Typescript: Day 6

В шестой задаче продолжаем улучшать функцию createRoute. Теперь нам нужно добавить ограничение generic типа. Решение в комментариях 👇

JavaScript Adept

05 Dec, 09:54


Advent Of Typescript: Day 5

Сегодня в задаче мы начинаем использовать generic типы. От нас требуется типизировать функцию так, чтобы ее возвращаемый тип совпадал с типом второго параметра. Решение в комментариях 👇

JavaScript Adept

04 Dec, 08:44


Advent Of Typescript: Day 4

Сегодня мы продолжаем работать с функцией из вчерашнего задания - теперь нам нужно немного расширить ее возможности. Решение в комментариях 👇

JavaScript Adept

03 Dec, 09:06


Advent Of Typescript: Day 3

На этот раз нас ожидает более «реальный» пример — нам нужно типизировать функцию, а конкретно — ее параметр input. С этого момента решение задачи я буду прикладывать первым комментарием к записи👇, дабы сразу его не спойлерить.

JavaScript Adept

02 Dec, 09:01


Advent Of Typescript: Day 2

Вторая по счёту задачка - в ней нам нужно создать литеральный тип "900_000":

Решение:
type Demand = 900_000;

JavaScript Adept

01 Dec, 16:58


🌲Помимо адвента по TS, стартанул adventofcode.com допускающий использование любых языков программирования.

Механика та же - каждый день новое задание. JetBrains, к слову, обещает приятные подарки лучшим, так что есть повод попробовать свои силы сразу в двух адвентах🥸

JavaScript Adept

01 Dec, 05:52


Advent of Typescript: Day 1

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

Сегодня и несколько следующих дней задачки будут совсем начального уровня. Сейчас нас просят создать тип.. number!

Решение:
type Demand = number;

JavaScript Adept

25 Nov, 09:32


Advent of Typescript

Совсем скоро (1 декабря) на typehero.dev начнется событие, приуроченное к концу года. Каждый день будет публиковаться задача по TS, и с каждым разом сложность будет расти.

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

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

JavaScript Adept

05 Sep, 07:51


Недавно появилась задачка: добавить автокомплит к полю с типом... string. Буквально, результирующий тип должен содержать все возможные строки, но при вводе определенных, обязан был включаться автокомплит. К примеру:
// хочу, чтобы автокомплитились слова "привет" и "пока"
const test: MagicType = 'пр';


Сначала я пришел к простому решению:
type Result = string | "привет" | "пока";


И все заработало. Но только в WebStorm (1 скрин). Зайдя в VSCode я обнаружил, что автокомплита все так же нет (2 скрин). Вот вам и особенности Language Server. Хотя, возможно влияют и какие-то локальные настройки редакторов. В любом случае, финальное решение, работающее в WS и VSC выглядит так:
type Result = (string & Record<never, never>) | "привет" | "пока";

Когда мы пишем какое-либо слово в строке, имеющей тип string, автокомплит редакторов предлагает нам символы, которые мы либо когда-то уже вводили, либо на основе каких-то своих предположений. Если же мы указываем тип как string & {} или string & Record<never, never>, то анализатор думает, что тип string сужается, и перестает сыпать свои подсказки. Хотя по факту, тип никак не изменился.

Когда мы добавляем к этому типу желаемые нами слова - (string & Record<never, never>) | "привет" | "пока", редакторы начинают предлагать только их, все по той же причине.

JavaScript Adept

13 Mar, 06:02


Наверно мало кто знает, но у меня есть ютуб канал. Раньше на нем были одни шортсы, но теперь я выложил "настоящее" видео на тему новой версии TypeScript.
Расскажу о новом utility типе, улучшениях анализатора и всяких плюшках.

https://youtu.be/XLEHL5M57mw

JavaScript Adept

13 Feb, 08:39


Хочу поделиться с вами одним полезным utility типом, который позволит превратить не очень читаемые пересечения в более читаемые "плоские" типы:
type IntersectionToFlat<T> = Omit<T, never>

Примеры до и после его использования прикрепляю. Теперь давайте разберемся, как он работает.

Omit - это встроенный utility тип в Typescript, который позволяет исключить из переданного ему типа нужные вам поля. Вторым аргументом он принимает пересечение из ключей, которые будут удалены из переданного первым типа:
type Omit<T, K extends PropertyKey> = { 
[P in Exclude<keyof T, K>]: T[P];
}

P in Exclude<keyof T, K> заставляет наш mapped type формироваться только на основе тех ключей T, которые не совпадают с переданным объединением K.

В нашем же случае мы передаем в качестве K - never, а значит мы просто пробегаемся по всем ключам T и создаем новый тип. В случае с пересечениями ключи будут получены от всех его участников, и получается, что мы как бы "сливаем" членов пересечения в один "flat" type.

В чем вообще разница между "flat" типом и пересечением? Пересечение { a: 1 } & { b: 2 } и "flat" тип { a: 1, b: 2 } считаются совместимыми, но не идентичными типами. У них не одинаковая структура, но они структурно эквивалентны.

Пример.

JavaScript Adept

08 Feb, 06:01


Про ревью.

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

Однако, спустя некоторое время я решил углубиться в эту тему и понял, что с этим не все так просто.

Правильное ревью в большой команде выстроить довольно тяжело. Часто мне попадаются ПРы в репозитории, которые я видел буквально раз в жизни, и в такие моменты я чувствую себя "умным линтером", который просто просканирует код на базовые дефекты, оставит Needs Work/Approved и уйдет в закат.

Да, у нас разделение на отдельные встраиваемые модули с кодовнерами. Но за одним этим модулем закреплена, как правило, лишь одна команда с парой разработчиков, а они вряд ли настругают тебе кучу ПРов за месяц, которые нужно будет отревьювить. В итоге мы приходим к парадоксу - опыт разработчиков полезен в ревью, однако действительно полезен он лишь в среде, где этот разработчик постоянно "обитает", ведь качественный анализ кода можно провести лишь запустив код с ПРа на фича-ветке, углубившись в задачу, почитав аналитику... А это трата большого кол-ва времени 🤦‍♂️

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

В целом, я считаю, что к ревью нужно подходить более ответственно и стараться избегать авто-аппрувов. Если ПР в какой-то бизнес-модуль из совершенно незнакомого вам отдела - можно просто пару раз пройтись глазами. Но если вы хотя бы раз взаимодействовали с этим модулем или он общий - лучше уделить ему побольше внимания. Как минимум - улучшите свой навык ревью, как максимум - найдете баг, или подчерпнете для себя что-то новое.

А как вы проводите ревью?

JavaScript Adept

29 Dec, 14:55


Приходите на стрим, где я совместно с UfoCoder и другими ребятами решаем задачки из Advent Of Typescript 2023)

https://www.youtube.com/watch?v=PGmfwmKGnsc

JavaScript Adept

27 Dec, 17:56


Ох, что-то я припозднился. Но задачки я все равно добью)

На этот раз у нас огромная 22 задача, валидация судоку. Прошу в статью: https://telegra.ph/Reshenie-22-zadachi-Advent-Of-Typescript-2023-12-27

JavaScript Adept

20 Dec, 17:24


20 день, 20 задача.

В этой задаче нам нужно превратить строку в "арт" вида ASCII.
Сегодня не успеваю расписать решение, поэтому без объяснений) Завтра дополню. Само решение:
type CommonLetterSize = [string, string, string]

type LetterToAsciiLine<
Str,
_Acc extends CommonLetterSize = ['', '', ''],
> = Str extends `${infer FirstChar}${infer Rest}`
? FirstChar extends keyof Letters
? LetterToAsciiLine<
Rest,
[
`${_Acc[0]}${Letters[FirstChar][0]}`,
`${_Acc[1]}${Letters[FirstChar][1]}`,
`${_Acc[2]}${Letters[FirstChar][2]}`,
]
>
: Rest
: _Acc;

type ToAsciiArt<
Str,
_Acc extends string[] = [],
> = Str extends `${infer FirstLine}\n${infer Rest}`
? ToAsciiArt<Rest, [..._Acc, ...LetterToAsciiLine<Uppercase<FirstLine>>]>
: Str extends `${infer Line}`
? [..._Acc, ...LetterToAsciiLine<Uppercase<Line>>]
: _Acc;

JavaScript Adept

19 Dec, 08:13


19 день, 19 задача.

Условие этой задачи я перечитал несколько раз. В итоге сначала все равно решил не так, как задумано, и потратил минут 10 на отладку 💀

Разложил решение в статью, в пост не умещается:
https://telegra.ph/Reshenie-19-zadachi-Advent-Of-Typescript-2023-12-19

JavaScript Adept

18 Dec, 07:21


18 день, 18 задача.

На этот раз нам нужно узнать количество определенных элементов в массиве. В целом, задачи пока не становятся сложнее.

Давайте создадим вспомогательный тип AddOne, который будем использовать в счетчиках для увеличения их на 1:
type AddOne<T extends number, _I extends 0[] = []> = 
_I['length'] extends T ? [..._I, 0]['length']
: AddOne<T, [..._I, 0]>;

Просто увеличиваем _I добавляя в него по одному элементу до тех пор, пока он не станет равен T, а затем возвращаем длину этого массива, добавив еще один элемент (чтобы его length стало больше T на 1).

Переходим непосредственно к задаче. На вход наш тип принимает T как массив, в котором будем осуществляться поиск, и F, как элемент, количество которого нужно посчитать:
type CountHelper<
T extends unknown[],
F,
_I extends number = 0
> = T extends [infer First, ...infer Rest]
? First extends F
? CountHelper<Rest, F, AddOne<_I>>
: CountHelper<Rest, F, _I>
: _I;

Извлекая первый элемент из итерируемого массива, проверяем, является ли он подтипом нужного нам элемента. Если да, то продолжаем считать дальше, увеличивая счетчик на 1 с помощью нашей утилиты. Если же нет - просто так же считаем дальше, но не увеличивая счетчик. Когда массив кончится, вернем наш счетчик _I.

Ну и так как по условию нужно, чтобы наш тип принимал только 2 аргумента, обернем его в еще один:
type Count<T extends unknown[], F> = CountHelper<T, F>

JavaScript Adept

17 Dec, 06:23


17 день, 17 задача.

Нужно определить кто выиграет в игру "Камень-ножницы-бумага". Задача довольно нетривиальная, столкнулся с подобным в первый раз.

Имеем тип с возможными знаками:
type RockPaperScissors = '👊🏻' | '🖐🏾' | '✌🏽';


Следуя правилам из описания, создаем мапу, в которой обозначаем, кто кого выигрывает:
type Conditions = {
'👊🏻': '✌🏽',
'🖐🏾': '👊🏻',
'✌🏽': '🖐🏾',
};

На скорую руку ничего кроме этого в голову не пришло. Условия ведь все равно где-то нужно описать, иначе вроде, не получается.

Далее легко:
type WhoWins<P1 extends RockPaperScissors, P2 extends RockPaperScissors> = 
P1 extends P2
? 'draw'
: P1 extends Conditions[P2] ? 'win' : 'lose'; //

Играем мы от лица второго игрока. Сначала проверим, одинаковые ли выпали знаки, если да - ничья. Иначе, если знак первого игрока проигрышный (подходит по значению в мапе при обращении через 'P2') - отдаем 'win' а в любом другом случае - 'lose'.

JavaScript Adept

16 Dec, 09:40


16 день, 16 задача.

Как я не старался уместить все в пост, у меня не вышло. Поэтому оформил в виде статьи. Задачка интересная, поиск по матрице, советую глянуть:

https://telegra.ph/Reshenie-16-zadachi-Advent-Of-Typescript-12-16

JavaScript Adept

15 Dec, 09:04


15 день, 15 задача.

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

Для начала создадим вспомогательный тип который будет создавать кортеж с заданной длиной и наполнять его переданным элементом:
type CreateTupleWith<E, L extends number, _Acc extends E[] = []> = 
_Acc['length'] extends L ? _Acc : CreateTupleWith<E, L, [..._Acc, E]>


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

Теперь нам нужно разобраться, как создать несколько таких кортежей и засунуть в объединение.

Вот полное решение:
type CreateTupleWith<E, L extends number, _Acc extends E[] = []> = 
_Acc['length'] extends L ? _Acc : CreateTupleWith<E, L, [..._Acc, E]>

type BoxToys<T, C extends number> =
C extends C ? CreateTupleWith<T, C> : never;


Тип BoxToys принимает на вход элемент, которым нужно заполнить кортежи, и объединение чисел, обозначающее их длину.

Используя дистрибутивность, мы можем "перебрать" наше объединение. Вот небольшой пример для понимания "C extends C":
type Hey<T extends string> = T extends T ? `${T}_lol` : never;

// "typescript_lol" | "is_lol" | "awesome_lol"
type Test = Hey<'typescript' | 'is' | 'awesome'>


Если мы начинаем использовать extends с объединением, то оно становится дистрибутивным, то есть каждый его элемент проходит проверку через extends. Получается, мы проверяем, каждый ли элемент объединения является подтипом каждого элемента его же самого, таким образом создавая перебор по всем элементам.

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

JavaScript Adept

14 Dec, 09:45


14 день, 14 задача.

На этот раз мы должны создать юнион из строки, разбив ее по слэшам ('timmy/jimmy' -> 'jimmy' | 'timmy'). Воспользуемся рекурсивными типами, infer, и особенностью never.

Решение будет таким:
type DecipherNaughtyList<T, _U = never> = 
T extends `${infer First}/${infer Rest}`
? DecipherNaughtyList<Rest, _U | First>
: T | _U;


T будет принимать в себя строку, а _U - будет являться аккумулятором, который будет накапливать в себе членов объединения. Так как мы не можем знать заранее результат распарса строки, то зададим значение по умолчанию как never (которое в тсе по факту является пустым объединением). И теперь, добавляя новых участников в объединение, never оттуда просто испарится.

Далее проверяем строку по паттерну, где в ней должно быть хотя бы 2 слова разделенных слэшем. Если они есть, то первое слово мы отдаем в аккумулирующий юнион, а все остальное (кроме слэша) отдаем обратно на вход в тип. В один момент на вход придет всего лишь одно, а не два слова, и для этого случая у нас расписана другая ветка условного типа, где мы просто вернем это слово как участника объединения с аккумулятором. Это сработает и в случае, если изначально к нам пришло всего одно слово, ведь тогда оно станет объединением с never, которое в итоге просто пропадет.

JavaScript Adept

13 Dec, 09:54


К слову про рекурсивные типы. Наверняка вы когда-то натыкались на ошибку Type instantiation is excessively deep and possibly infinite. Обычно это значит, что либо операция слишком тяжелая и может серьезно повлиять на производительность вашей IDE, либо вообще бесконечная.

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

Хороший пример хвостовой рекурсии в типах описывается в репозитории Typescript на GitHub.