Будни разработчика
14.7K subscribers
1.17K photos
334 videos
7 files
2.01K links
Блог Lead JS-разработчика из Хельсинки
Автор: @bekharsky

По рекламе: https://telega.in/channels/htmlshit/card?r=GLOiHluU или https://t.me/it_adv

Чат: https://t.me/htmlshitchat

№5001017849, https://www.gosuslugi.ru/snet/679b74f8dad2d930d2eaa978
Download Telegram
#стрим дня

…который будет 6 сентября.

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

И у него на стриме 6 сентябра будет создатель курсов по TypeScript, Мэтт Покок: https://www.learnwithjason.dev/advanced-typescript-let-s-learn-generics

Тема стрима — дженерики в TS. Без вопроса по дженерикам не обходится ни одно интервью.

Залетаем, смотрим, учимся.

#ts #generics
2👍1
#инструмент дня

Кто-нибудь ещё считает, что подсказки и ошибки TypeScript страшно раздражают своей формальностью?

Say no more!

Появилось расширение, переводящее их на человеческий (ну… английский) язык!

КДПВ говорит сама за себя, а пока дам ссылку: https://marketplace.visualstudio.com/items?itemName=mattpocock.ts-error-translator

Ну и песочница, чтоб побаловаться и понять: https://ts-error-translator.vercel.app/

Отправьте ей свою ошибку и давайте посмотрим на результат.

P. S. а ошибки типов Styled Components оно переведёт, как считаете?

#vscode #ts #typescript
👍16👎3🔥2😁1
#заметка дня

Что делать, если нужная вам библиотека не предоставила типы для всех публичных методов?

Ну вот такое вот архитектурное решение: метод экспортирован из модуля, а тип или интерфейс — нет?

Делать unknown или any? Копировать и переопределять с помощью as?

Ни в коем случае! Вам нужен простой советский... ReturnType: https://www.typescriptlang.org/docs/handbook/utility-types.html#returntypetype

Пример использования — на иллюстрации. Ну или ещё можно так:

const createPerson = () => ({
firstName: 'John',
lastName: 'Doe'
})

type Person = ReturnType<typeof createPerson>


Не делайте ерунды, котаны. Читайте документацию.

#typescript #ts #types
🔥25👍3
#такое дня

Дежурное напоминание о том, что перфекционизм убивает мотивацию и не боги горшки обжигают.

Откуда скриншот? Да из типов для React: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/index.d.ts#L1194

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

P. S. обновил ссылку до React 18.

#ts #react
😁5👍3
#расширение дня

Ну что, пришло время поговорить о серьезных вещах!

А что может быть серьезнее ошибок TypeScript? Правильно, ничего. Уж слишком они формальные. И мы на самом деле уже с вами обсуждали расширение, переводящее их в человеческий вид: https://t.me/htmlshit/1601

И вот есть ещё одно, стремительно набирающее популярность: https://github.com/yoavbls/pretty-ts-errors

Ссылка на VS Code store: https://marketplace.visualstudio.com/items?itemName=yoavbls.pretty-ts-errors

Работает с Node и Deno, React и Vue, Solid и Qwik. Далее везде, в общем.

Пробуем, рассказываем о результатах.

Прокачивайте скиллы, котаны.

#typescript #ts #vscode
7🔥3👍1
#инструмент дня

Миграции с JavaScript на TypeScript часто заходят не туда. Или не заканчиваются. Или откатываются. Зачастую в лучшем случае весь новый код начинает писаться на TypeScript и всё на этом.

Да, ещё не факт, что оно вам надо вообще, но это тема иной беседы.

Итак, с чего же начать вашу миграцию если автоматизированные средства, расставляющие везде any, вам не подходят, а //@ts-nocheck — для быдла?

Начните с модулей с наибольшим числом зависимостей, говорит нам Matt Pocock, а он в общем-то в курсе, о чём утверждает, он учит людей тайпскрипту фулл-тайм :)

А как найти эти самые модули? Очень просто, возьмите построитель диаграмм зависимостей. Madge вполне подойдёт: https://github.com/pahen/madge

Ну и никто не мешает весь новый код писать сразу на TS.

#typescript #javascript #ts #js #diagram #deps
🔥91
#фишка дня

Мой любимый фронтенд-твиттер Кричащий Банан показал очень интересную штуку: тип Simplify<T>, взятый из https://github.com/sindresorhus/type-fest (отличная репа, кстати, куча полезных типов).

Что это и зачем? Я вот сразу не понял. Ну, ты же сам обычно типы пишешь, зачем упрощать? А все просто.

Мы предполагаем, что A — собирательный тип. Например, комбинация из Partial и Omit, что достаточно часто приходится делать:

type A = { foo: string; bar: number };
type B = Omit<A, "bar"> & Partial<A>

И IDE покажет... ну, бестолковую фигню покажет, которая не даст вам никакой информации. Как этого избежать? Можно B... упростить. И будет красиво! Смотрим КДПВ или вот, ссылку на песочницу, сразу ясно что к чему.

Не забываем посылать спасибы авторам Type Fest и Кричащему банану :)

#typescript #ts
👍19
#заметка дня

Очень много сказано на тему того, что использовать тип React.FC для функциональных компонентов React довольно неудобно, глупо и вообще странно: передача children была обязательной, нельзя было вернуть string, number, undefined (что поддерживается JSX так-то).

Самое странное, что он по-умолчанию был в create-react-app и вводил в ступор огромное количество разработчиков вне зависимости от их опыта: https://github.com/facebook/create-react-app/pull/8177

Начиная с TS 5.1 и React 18.1 использование React.FC безопасно и прозрачно, но рекомендованным способом пока, конечно, остаётся прямая типизация пропсов, смотрим PR выше.

В общем, don't overengineer it, котаны.

#react #fc #ts
🤔2🗿2
#заметка дня

Что делать, если нужная вам библиотека не предоставила типы для всех публичных методов?

Ну вот такое вот архитектурное решение: метод экспортирован из модуля, а тип или интерфейс — нет?

Делать unknown или any? Копировать и переопределять с помощью as?

Ни в коем случае! Вам нужен простой советский... ReturnType: https://www.typescriptlang.org/docs/handbook/utility-types.html#returntypetype

Пример использования — на иллюстрации. Ну или ещё можно так:

const createPerson = () => ({
firstName: 'John',
lastName: 'Doe'
})

type Person = ReturnType<typeof createPerson>

Не делайте ерунды, котаны. Читайте документацию.

#typescript #ts #types #бородач
👍314
#заметка дня

Как же у меня бомбит сейчас, вы бы знали. Но обо всём по порядку.

Кому-то из разработчиков JetBrains показалось полезным дать возможность скомпилировать все TypeScript файлы разом в JavaScript и положить их рядышком с оригиналами.

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

И, естественно, класс Config.ts перезаписал собой файл конфигурации config.js 🫠

Хвала небу, в PhpStorm есть локальная история файла, потому что конфигурация, естественно, исключена из Git.

Моя жопа давно так не горела. Это ну вообще как? Зачем? Кому это может быть полезно?! Ну типа, должна же быть хоть какая-то веская причина так делать?

Я сейчас отдышусь и передам свои впечатления команде поддержки JetBrains, которые помогают нашей компании. Но не сразу, точно не сразу...

#typescript #jetbrains #надмозг
👍14🤬10
#такое дня

Дежурное напоминание о том, что перфекционизм убивает мотивацию и не боги горшки обжигают.

Откуда скриншот? Да из типов для React: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/index.d.ts#L1258

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

P. S. обновил ссылку до React 18. До сих пор там.

#ts #react #бородач
7
This media is not supported in your browser
VIEW IN TELEGRAM
#библиотека дня

Соскучились по хорошим дейтпикерам? Да по ним невозможно не соскучиться.

Итак, сегодня на обзоре https://easepick.com/

Чем хорош?

- Нет зависимостей
- Легковесный
- Написан на TypeScript
- Есть удобный конфигуратор
- Поддержка плагинов, изначально поставляется с прицелом на их использование

Стандартный набор любой библиотеки в 2023 году, в общем.

Чем плох?

- Мне не понравился выбор года. Ну его вообще нет, если на то пошло. В этом отношении bootstrap-datepicker непобедим.

Но это ерунда, на самом деле. Если посмотреть на примеры, становится ясно, куда ребята целились: в создание удобного отельного дейтпикера. И им определённо удалось!

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

Конфигуратор песня, конечно: https://easepick.com/configurator/

В общем, моя рекомендация.

#datepicker #js #ts
👍30
#заметка дня

Что делать, если нужная вам библиотека не предоставила типы для всех публичных методов?

Ну вот такое вот архитектурное решение: метод экспортирован из модуля, а тип или интерфейс — нет?

Делать unknown или any? Копировать и переопределять с помощью as?

Ни в коем случае! Вам нужен простой советский... ReturnType: https://www.typescriptlang.org/docs/handbook/utility-types.html#returntypetype

Пример использования — на иллюстрации. Ну или ещё можно так:

const createPerson = () => ({
firstName: 'John',
lastName: 'Doe'
})

type Person = ReturnType<typeof createPerson>

Не делайте ерунды, котаны. Читайте документацию.

#typescript #ts #types #бородач
👍191
#заметка дня

Хочу сказать пару слов про файлы-реэкспорты aka index.js. В англоязычном сегменте разработчиков их ещё бочками зовут — barrels. Кори Хауз сегодня в тви напомнил :)

Задачи бочки просты:
Сократить путь импорта
Дать возможность импортировать несколько модулей из одного места
Предоставить некий публичный контракт.

Мол, если вы используете что-то не задекларированное в index.js — сами себе злобные буратины.

Вот только минусов-то побольше будет:
🚫 Раздувает бандл, ведь тришейкинг становится невозможным
🚫 Потребление памяти тоже возрастает, обработать-то файл надо
🚫 Замедляет сборку
🚫 Мешает навигации по коду, когда опция "перейти к определению" кидает тебя в реэкспорт.

В общем, я перестал создавать их и планирую грохнуть из существующего кода.

Вот правило для ESLint чтобы найти у себя это в коде и больше так не делать: https://github.com/christianvuerings/eslint-plugin-no-re-export, там же есть и побольше информации.

А у вас дела как, котаны?

#js #ts #module
16👍5
#тип дня

Попробую вместо тега #фишка внести что-то новое для TypeScript. Пусть будет "тип", а там посмотрим.

И сегодня на повестке дня запрет определённых ключей при передаче объекта. Например, контекста в трекинге или тех же пропсов в React.

Как правило, контекст в трекинге передаётся в функцию-хелпер, а после — дополняется какими-то переменными среды. Как-то так:


function logEvent(severity: LogSeverity = "info", context: LogContext = {}) {
log(severity, {
type: "event",
userId: "rick@ro.ll",
env: "dev",
...context,
})
}


Понятное дело, нам совсем неохота, чтобы кто-то случайно передал в контекст type, userId или env и затёр всё нафиг.

"Погоди, так разверни контекст повыше", — скажет кто-то умный.


Ну так тоже не стоит, мы не хотим сюрпризов уже на разборе полётов логов в случае инцидента, когда ожидалось одно, а на выходе — другое.

Проще сразу не дать сделать странное, как минимум на этапе введения контекста. Наш вариант в итоге стал таким:

type LogContextDefaults = "type" | "userId" | "env";

type LogContext = {
[key: string]: string;
} & {
[key in LogContextDefaults]?: never;
}


Ну и конечно, ссылка на песочницу: пуньк.

Есть предложения получше, котаны? Ну кроме проверки в рантайме :)

#typescript #ts #type
15👍1🤩1
#инструмент дня

Я сегодня решил написать пост, не отходя от кассы.

Итак, преамбула: проект развивается 10 лет, с 2014 года. Стек: Google AppsScript, JavaScript, TypeScript, jQuery, React, RSPack.

Когда я пришёл в 2019 году в компанию, основной JS-файл представлял из себя монстра на 40 000 строк. Да-да, монолитный файл, в котором люди ворочали селекторы с помощью jQuery!

И это только фронтенд-часть.

Сказать, что я офигел, устроившись на работу, это ничего не сказать. Но что делать.

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

С последним редизайном проект окончательно перешёл на React в смысле рендера (не бизнес-логики и контроля, но это поправимо).

Поскольку, переход произошёл довольно таки радикально, с созданием новой точки входа (entrypoint), естественно, образовалась куча мёртвых экспортов, от которых даже WebStorm-у немного стало плохо.

И сегодня я, наконец, разрулил эту проблему инструментом Knip: https://knip.dev/

Отображает все мёртвые (неиспользуемые) файлы, мёртвые экспорты, зависимости. Легко конфигурируется, хотя прекрасно работает и без этого (конфигурация заключается в настройке точек входа, если у вас дефолтные — не парьтесь).

Итог — минус 10000 строк и 30 неиспользуемых файлов. Считаю, отличный результат. Стало легче ориентироваться.

Ну и по пути нашёл файл, который так-то стоило бы вернуть в оборот... 🫠

Прежде чем вы скажете, что это задача ESLint, вот, почитайте: https://github.com/typescript-eslint/typescript-eslint/issues/371

ESLint справляется с определением мертвых импортов, но никак не экспортов и файлов вообще. И было решено этого не делать.

В общем, естественно, рекомендую, котаны!

#js #ts #cleanup #unused
Please open Telegram to view this post
VIEW IN TELEGRAM
👍372
#новость дня

В node.js появилась экспериментальная нативная поддержка TypeScript!

Крепко же их bun приложил...

Ссылка на PR: https://github.com/nodejs/node/pull/53725

По факту происходит отбрасывание типов, поэтому средства вроде Enum и namespace не поддерживаются. Инициатива предоставления стабильного API поверх TypeScript получила название amaro и в дальнейшем планируется выделение в отдельный обновляемый модуль. Работает (кто бы сомневался) при помощи swc, собранного в WebAssembly!

Так что никаких больше ts-node!

node main.ts

...и поехали!

#node #typescript #ts
17🤩3👍1🤬1
#заметка дня

Как мы все знаем, в JavaScript есть две формы «пустоты»: undefined и null.

Но почти весь современный фронтенд давно выбрал сторону.

null — это ошибка на миллиард долларов, о которой пожалел даже его создатель, Тони Хоар. Он добавляет путаницу, ломает API и заставляет писать лишние проверки.

Почему null — плохая идея:
Разные API возвращают то null, то undefined, то оба.

Это неясно, это ошибки.

TypeScript-гайд от Microsoft прямо говорит: используйте undefined, избегайте null.

В TSLint null запрещён по умолчанию (`no-null-keyword`).

Правила ESLint Unicorn (да, название неслучайное) — тоже пропагандируют борьбу с null в пользу чистого, предсказуемого кода.

В крупных экосистемах, например, как у Prisma, null создаёт баги и недопонимание в API (issue #572)

undefined — поведение по умолчанию в JS для необъявленных свойств и пустых объектов.

undefined выигрывает даже в JSON. Когда ты сериализуешь данные:

null остаётся в объекте:


{ "a": null }


а undefined просто исчезает:


{ "a": undefined } → { }


В реальных системах это даёт выигрыш в размере и читаемости. Пример из продакшена: объект с миллионом null весил 13.9 MB, а с undefined — всего 21 байт. И если ты работаешь с Node.js и хорошо контролируешь свои API — undefined тебе только на руку.

Да, из-за того, что множество систем до сих пор оперирует null, и даже DOM API возвратит null при отсутствии элемента (ноды), выбор становится не настолько простым. К счастью, мы можем использовать optional chaining (?.) и nullish coalescing (??) чтобы снизить вероятность конфуза.

Кстати, даже столь любимый мной Effector ещё не так давно пропагандировал null для пустых сторов, но с недавнего времени разрешил undefined (в своей манере, там сложная концепция).

Итак, null — это рудимент. Он создаёт больше проблем, чем решает. undefined уже делает всё, что нужно — чище, предсказуемей и легче.

Пора выбрать сторону 🦄

P. S. человек, который заставил меня принять сторону сейчас, наверное, сидит и хихикает. Но в целом, единственное, что у меня есть в защиту null — это наш бакенд на PHP и MySQL 🤷

#js #ts #eslint #null
👍29👎9🤩5
#фишка дня

Как убедиться, что ваш switch покрывает все кейсы, а default остаётся только на случай косяков в рантайме?

Очень просто! Два варианта:

1. Используем never, от Кори Хауса:

interface Dog {
kind: "dog";
favoriteToy: string;
}

interface Parrot {
kind: "parrot";
knowsWords: number;
}

type Pet = Dog | Parrot;

function logPetTalent(pet: Pet) {
switch (pet. kind) {
case "dog":
return console. log (Dog loves ${pet. favoriteToy}. ');
case "parrot":
return console. log (Parrot knows ${pet.knowsWords} words. *);
default:
const exhaustiveCheck: never = pet;
}
}


2. Второй вариант — ESLint и правило switch-exhaustiveness-check. И уже дальше решаем, разрешать default, или нет. Конфигураия это позволяет.

Впрочем, мы у себя вообще переходим на pattern matching, о чём как-нибудь в следующий раз.

#ts #switch
👍104🫡1
#фишка дня

В TypeScript получение keyof [string, number] кортежа даст не "0" | "1", а:
"0" | "1" | "length" | "push" | "toString" | ...

Пу-пу-пу...

Хочешь только индексы?


type IndexKeys<T extends any[]> = Extract<keyof T, `${number}`>;


Теперь IndexKeys<[string, number]> → "0" | "1".

Проклято.

#ts #keyof
👍22🤡21