Будни разработчика
14.6K subscribers
1.14K photos
319 videos
7 files
1.96K 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
#codepen дня

Я, стыдно признаться, очень мало работал с горячими клавишами в вебе. И до недавнего времени даже не мог сказать, в чём отличие which, code и key.

Но для моего пет-проекта пришлось немного покурить тему. И в итоге я наткнулся на прекрасный пример, поясняющий за всю хурму: https://codepen.io/denilsonsa/pen/epmoma

И key, и code, и repeat, и модификаторы, и, собственно, события. Прекрасно.

Конечно же стоит обратить внимание и на MDN: key, code. Там тоже есть интерактивные примеры.

А в пет-проекте было просто прекрасное. Я разрабатываю расширение для Chrome DevTools и никак не мог понять, почему я не могу перехватить стандартные клавиатурные сочетания. Даже чёртов Esc открывал консоль.

А всё оказалось просто: я привык вешать слушатели на window или document, а ребята из Chrome повесили их на document.body. Просто прекрасно.

Впрочем, победить получилось и теперь моё расширение управляется с клавиатуры на ура.

#key #code #js #javascript #hotkeys #keyboard
#заметка дня

Если вы работаете с TypeScript, то не может не замечать, что постоянно типизировать события может быть мучением. Тип события, таргета, значения... Как же быть? Можно как-то перестать повторяться?

Ответ прост, подписчик @glebcha предлагает служебный тип!

import * as React from 'react';

type PaymentMethodKind = 'credit_card' | 'direct_debit'

interface OverrideEventValue<E = HTMLElement, V = string> extends React.ChangeEvent<E> {
target: EventTarget & E & { value: V }
}

const handleChange: React.ComponentProps<'input'>['onChange'] = ({ target }: OverrideEventValue<HTMLInputElement, PaymentMethodKind>) => {
const paymentMethod = target.value;

console.log(paymentMethod);
}

Красиво, декларативно, удобно.

Ну и куда же без ссылки на TS playground.

Ждём контрпредложения в комментариях :)

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

Я обещал про нытьё тимлида. Ну давайте поноем.

Технически, я engineering manager, что чуть больше, но то такое.

Итак, должен ли тимлид/менеджер кодить?

Короткий ответ: да. Но вот что и когда? И вот тут я начинаю сыпаться.

Почему люди (ну, кодеры) становятся тимлидами (или менеджерами)? Потому что, внезапно, они были достаточно хороши в своей работе по мнению вышестоящих менеджеров и/или инженеров.

Что это значит? Что они достигли максимального уровня комфорта, компетентности, если хотите.

Ещё можно сказать, что они освободили место для кодеров получше.

Естественно, отсюда вытекает следующее: когда у меня типично тимлидские проблемы, мне хочется просто уйти с головой в какую-нибудь интересную (или просто долгую) задачу. Но это путь в никуда.

Болото кода может засосать глубоко и надолго. А проблемы клиентов, митинги, планирование — никто не отменял. В итоге не получается нормально ни там, ни там.

Совсем без планирования тоже нельзя, можно в итоге упустить нечто важное. Потому, нужна категоризация кодерских задач для тимлида.

Аврал я помещать в категории не буду. Его наличие означает, что уже всё.

1. Есть свободное окно. Планы закреплены, команда занята, довольна и понимает, что делать, цели намечены — ок, можно продолжать.
2. Ты лучший в том, что собрался делать. Можешь качественно и быстро сделать задачу — вперёд, но не забывай о процессах. Будь примером.
3. Не блокируй критичные узлы. Условно, не надо лезть в аутентификацию, а потом спрыгивать с неё, как будто ничего не было, вешая задачу на кого-то из команды. Не смог спланировать правильно — не начинай.

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

Справляюсь ли я с этим на данный момент? Хороший вопрос! Но я пытаюсь, правда :)

#teamlead #manager #code #problems
#фишка дня

Мой любимый фронтенд-твиттер Кричащий Банан показал очень интересную штуку: тип 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
#фишка дня

Кричащий банан 🍌 (да-да) принёс шикарную фишку TypeScript: как типизировать ключ объекта без каста.

Решение: заранее объявите тип переменной перебора как keyof typeof. Всё, вы великолепны.

Пруф на TS Playground.

#typescript #cast
#заметка дня

Итак, объяснение результатов опроса выше. Поехали.

Пока я думал, как написать, и искал время, в комментариях к опросу уже всё сделали. Аж дважды. И даже обняться успели.

Потому, буквально, цитата:

1) Что такое тип Omit? А вот что:


type Omit<T, K extends string | number | symbol> = { [P in Exclude<keyof T, K>]: T[P]; }


2) Видим, что там есть Exclude<keyof T, K>. Пока все безобидно, куда же делись наши поля?

3) Обращаем внимание на keyof T. Эта конструкция возвращает все ключи, что есть у объекта и к которым можно обращаться без ошибки (!).

И именно здесь отсекаются все поля, которых нет во всех наших типах объединенных через символ | (Union) потому что мы не можем обращаться к полям "price" и "name" без доп. проверок (type guards)

Автор комментария не против, если что 🙂

А для всех заинтересованных, вот ссылка на TS Playground.
#тип дня

Попробую вместо тега #фишка внести что-то новое для 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
#тип дня

Типом дня назначается вон тот в кожаной куртке. Да-да, о тебе говорю!

TL;DR: вычисленный тип функции с дженерик-аргументом можно сузить, декларируя тип как const.

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

Непонятно? Давай так:

function wrapNames <T>(names:T[]) {
return names.map<{name: T}>(name => ({name}));
}

const [first, second] = wrapNames(['Sergey', 'Alex'])


Какой вычисленный тип получат first и second?

Очевидно, такой:

{
name: string;
}


Но... это же довольно неудобно. А если мы схему валидируем, например? Или форму описываем. Или тесты пишем в конце-концов. Почему при заранее известном константном типе входных данных мы получаем вычисленную строку?

К счастью, начиная с TypeScript 5.0 можно объявлять дженерики как константные типы! Иначе говоря, работать с максимально узкими (narrow) типами. Это, собственно, аналог указания as const у предопределённой константы.

Использование простое:

function wrapNames <const T>(names:T[])

И на выходе тип у first и second будет такой:

{
name: "Sergey" | "Alex";
}


Песочница: пуньк.

Давайте исходя из полученной информации создадим валидатор по известной схеме данных и дальше поработаем с ним:


// initValidator
function parse<const T extends { name: string; surname: string }>(users: T[]) {
return (name: T['name']) => users.find(u => u.name === name)?.surname;
}

// validate
const getSurname = parse([
{
name: 'Joe',
surname: 'Doe',
age: 30,
},
{
name: 'John',
surname: 'Dohn'
}
]);


Определение getSurname приобретёт такой вид:

getSurname: (name: "Joe" | "John") => string | undefined


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

Теперь это вполне себе напоминает удобную работу с коллекциями данных, не стыдно и форму провалидировать.

Песочница: пуньк.

Штука относительно новая, к ней ещё придётся немного привыкнуть. Но уже можно пробовать применять.

Ах да, ссылка на соответствующий PR в TypeScript: https://github.com/microsoft/TypeScript/pull/51865

#typescript #generic #const
#тип дня

Кричащий банан 🍌 (да-да) принёс шикарную фишку TypeScript: как типизировать ключ объекта без каста.

Решение: заранее объявите тип переменной перебора как keyof typeof. Всё, вы великолепны.

Пруф на TS Playground.

#typescript #cast #бородач
#тип дня

Говоришь такой коллеге: «Ты зачем столько классов насоздавал для такой простой вещи? Используй CSS-переменные».

А он приходит к тебе с картинкой выше и грустный весь.

Что мы делаем в таком случае?

Да очень просто, дополняем интерфейс CSSProperties, чтобы до реакта, наконец, дошло:


declare module 'react' {
interface CSSProperties {
[key: `--${string}`]: string | number
}
}


Пруф: песочница.

Странно, конечно, что это не из коробки всё.

#css #types #react #typescript
#тип дня

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

Не делайте это ручками, используйте служебный тип Parameters!


const createPerson = ({
firstName,
lastName
}: {
firstName: string,
lastName: string
}) => ({
firstName,
lastName
})

type CreatePersonParams = Parameters<typeof createPerson>[0];

const params: CreatePersonParams = {
firstName: 'John',
lastName: 'Doe'
};


Песочница

#typescript #utility