TGViewer
Channel Public Channel
Кузенков x IT-Хозяева

Кузенков x IT-Хозяева

@webcorner

Посты о всяких технологиях и не только. @bondiano
Subscribers
333
Photos
2
Videos
0
Links
32

Showing posts older than #44 · Back to latest

Older Posts 9 shown
Post #41 396
Второй подход к снаряду поиска типов

Сколько же решений по типизации экшенов выдает Stackoverflow, вплоть до пакетов с тремя звездочками на Github и каждый способ не лишен изъянов.
Возьмем например довольно очевидный способ создания экшена "в лоб":

interface Action<T, P> {
readonly type: T;
readonly payload?: P;
}

const createAction = <T extends string, P>(type: T) => (payload: P): Action<T, P> => {
return { type, payload };
}

В таком случае от нас TS будет требовать пробрасывать вторым аргументом в дженерик payload, что нам не всегда нужно (тк нельзя в дженерик с двумя аргументами передавать только один, ну и если попробуем от этого избавиться, то все равно в конце концов мы теряем тип от type).
И по общей договоренности с командой мы решили, что тип поля type обязательно должен быть строкой.

И так, приступим исправлять типизацию.

Что же для нас такое этот createAction в итоге.
Это функция, принимающая type и payload и возвращающая объект с этими двумя полями, а главное сохраняющая типы.

const createAction = (type) => (payload) => ({
type,
payload,
})

И первый тип, с которым надо нам надо разобраться - это тип Action, содержащий payload, type и более базовый тип - ReduxAction (для него достаточно поля type)

interface ReduxAction<T extends string> {
type: T
}

export interface Action<Type extends string, Payload = void> extends ReduxAction<Type> {
payload?: Payload
}

Тут все довольно просто. Обернем в это createAction.

const createAction = <T extends string, P>(type: T) => (payload?: P): Action<T, P> => ({
type,
payload,
})

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

export const createAction = <A extends Action<A['type'], A['payload']>>(type: A['type']) => (payload: A['payload']) => ({
type,
payload,
})

Теперь у нас выводится типы тайпсрипта по типу экшена. Так мы не можем доказать тайпчекеру, что тип у A['type'] - строка, и это не сработает (пример). Впрочем, мы и не пытались.
Чтобы это сделать мы воспользуемся таким вот хитрым хелпером, который позволит доказать, что тип действительно строка (если у вас есть идеи как это сделать проще - велком в личку).

type StingTypeField = {
type: string
}

type LookupStringTypeField<T, K> = K extends keyof T ? (T extends StingTypeField ? T[K] : never) : never

В LookupStringTypeField мы говорим, что поле в объекте точно есть поле K и оно точно строка, а иначе и не может быть (never).

Итоговый createAction:

const createAction = <A extends Action<LookupStringTypeField<A, 'type'>, A['payload']>>(type: A['type']) => (payload: A['payload']) => ({
type,
payload,
})

И конечно ссылка с примером.

P.S. В примерах не исключены ошибки ☺️

Всех с наступающим!🎄
#w_corner_ts
www.typescriptlang.org TS Playground - An online editor for exploring TypeScript and JavaScript The Playground lets you write TypeScript or JavaScript online in a safe and sharable way.
Post #40 382
TSLint to ESLint

TSLint обещает скоро уже стать deprecated. И уже пора бы переносить все проекты с ним на новые рельсы. Очевидно, что единсвенный выбор сейчас - ESLint. Ну и куда же в современном проекте без Prettier'а. Пройдемся немного по шагам, которые сделал я для подобного переноса.

1. Во первых, не хотелось переформатировать проект, потому я сразу пошел искать тулзу, которая мне переконвертирует все мои правила из TSLint, и супер, что такая уже есть - tslint-to-eslint-config.

Запускаем npx tslint-to-eslint-config

2. Тулза оказалась достаточно не плохой. Но все нужные пакеты для использования ESLint она сама не поставила :(. Поставим сами, за одно добавим Prettier. (хотя и создатель сказал не использовать, но я продлжаю выбирать yarn в качестве своего менеджера зависимостей, как минимум тк я подсел на workspaces)

yarn add eslint eslint-config-prettier eslint-plugin-prettier prettier @typescript-eslint/parser @typescript-eslint/eslint-plugin @typescript-eslint/eslint-plugin-tslint -D


3. Перименуем eslintrc.js в .eslintrc.js, либо придётся добавить флаг в скрипте -c eslintrc.js.

4. Обновим поле scripts в package.json.
"lint": "eslint --ignore-path .gitignore --ext .js,.jsx,.ts,.tsx"

5. Добавим Prettier. Сперва в массив plugins - plugins: [..., 'prettier'], затем в rules - rules: { 'prettier/prettier': 'error', ... }

6. Почему-то tslint-to-eslint-config генерирует несколько не сущесвующих правил в плагине @typescript-eslint/eslint-plugin, тч после первого запуска lint'a вам придется их подтереть или поискать альтернативы.

6. PROFIT! Можно удалять tslint и избавляться от правил из @typescript-eslint/tslint плагина.

Некоторые мои проекты хранятся в монорепо, тч для нормальной работы ESLint потребовалась небольшая докрутка: parserOptions.project в .eslintrc.js поддерживает также массив, тч потребовалось внести каждое приложение, содержащее tsconfig.json отдельно.

Также vscode сразу не хотел обрабатывать нужные конфиги, не беда, в issue к плагину vscode-eslint предложили рабочее решение.

P.S. Все же с tslint-to-eslint-config некоторые правила останутся не перенесеными, тч смотрим также созланный тулзой файл tslint-to-eslint-config.log.
#w_corner_ts
GitHub GitHub - palantir/tslint: :vertical_traffic_light: An extensible linter for the TypeScript language :vertical_traffic_light: An extensible linter for the TypeScript language - palantir/tslint
Post #38 397
keyof в поисках типа

Жили были функции без типов. Среди них были такие,
как prop, возвращающие поля из объекта.


const prop = (obj, key) => obj[key]

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


const prop = (obj: {}, key: any) => obj[key]
const example = { meow: 'meow' }
prop(example, 'meow') // type: any


"Как так!", — восклицали они, — "Ведь мы видели, что по ключам всегда получаем тип!"

И были правы. В TypeScript существует поиск типов (Lookup Types) , который и позволяет получить связанный с ключем тип.

Тогда к ним пришел важный тип и дал новое магическое слово - keyof.

Оно давало особую способность проникать в публичные имена ключей типов и создавать литеральный объединенный тип (Union).*


const example = { meow: 'meow', murr: 'murr' }
type CatSays = keyof typeof example // "meow" | "murr"


Благодоря знанию о keyof и Lookup Types они быстро сообразили, как затипизировать их функцию prop:


const prop<T, K extends keyof T>(obj: T, key: K) => obj[key]

Теперь они смогут спокойно жить и припевать в типобезопасном мире, типизируя любые функции, которые как-то получают только нужные поля объекта!

—
* с помощью keyof можно получить все публичные не статические ключи пренадлежащие именно типу, потому в примере мы вынужденны получать запросом типа typeof.
#w_corner_ts
Post #36 313
child_process

В ноде есть 4 способа запуска дочернего процесса:
child_process

.spawn - начинает асинхронно отдтавать данные сразу, как запустится процесс. Его выгодно использовать при работе с большими объемами данных, например, с огромными файлами или выводом результатов утилит вроде curl. Данные получаем при помощи event-listener'а(.on('data', () => {...})).

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

.execFile - как exec, но напрямую запускает исполняемый файл, без оболочки.

.fork - как spawn, но позволяет обмениваться сообщениями с дочерними процессами через функции send/on.
#w_corner_nodejs
Post #33 295
Деструктуризация динамических полей
Совсем недавно задумался над тем, как получать значение поля, которое было назначенно динамически.

const myActionName = 'ACTION'
const actionByActionName = { [myActionName]: action }

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

const { [myActionName]: actionName } = actionByActionName
Post #32 296
Чисто лол-кек, В Хроме, 6**9 === Math.pow(6, 9) //false.
Очередная крутая фича браузера и зачем нам эти браузерные войны в статье Саши Майорова.
https://medium.com/@frontman/funes-8-math-pow-не-равно-972955382594
З.Ы. в комментах-таки говорят, что в Chrome Nightly уже поправили
Medium FunES#8: Math.pow не равно** JavaScript зарисовки в стиле WTF
Post #31 312
Недавно столкнулся с проблемой, что требуется https для локальной разработки. Решение нашел довольно быстро: Ngrok. Это платформа с удобной CLI утилитой (написана, кстати, на node.js), которая позволяет организовать удалённый доступ на веб-сервер, ну или любой другой сервер, запущенный на ПК. ПК при этом может не иметь статического IP адреса или быть за NAT'ом.
Так что если вам нужно показать что-то локально поднятое, а ещё желательно, чтобы SSL или TLS сверху накинули эта тулза отлично подойдет.
https://ngrok.com/
#w_corner_tools
ngrok.com ngrok: deliver your apps, APIs, and AI on local and prod ngrok is developer infrastructure that routes and secures traffic to your apps, APIs, and AI models, wherever they run. Start on localhost, ship to prod.
Post #1
Channel created
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →