#фишка дня
Кричащий банан 🍌 (да-да) принёс шикарную фишку TypeScript: как типизировать ключ объекта без каста.
Решение: заранее объявите тип переменной перебора как
Пруф на TS Playground.
#typescript #cast
Кричащий банан 🍌 (да-да) принёс шикарную фишку TypeScript: как типизировать ключ объекта без каста.
Решение: заранее объявите тип переменной перебора как
keyof typeof. Всё, вы великолепны.Пруф на TS Playground.
#typescript #cast
👍22🤩4
#инструмент дня
Ладно, мы все можем согласиться, что у TypeScript замечательный офсайт, прекрасная документация и удобная песочница, но в мире есть сотни тысяч JS-библиотек и десятки тысяч из них либо написаны на TS, либо имеют выделенные типы.
А единой документации по этим типам нет!
Точнее, не было, но теперь вышел https://tsdocs.dev/
Это система поиска по существующим пакетам с типами. Она установит нужный пакет, распарсит типы и JSDoc и отобразит в удобном формате.
К слову, котаны, что вы предпочитаете по cmd- (ctrl)-click в редакторе? Прыгать к имплементации, или к типам?
Я вот — к имплементации, прыгать к типам меня раздражает.
#typescript #dts #types #doc
Ладно, мы все можем согласиться, что у TypeScript замечательный офсайт, прекрасная документация и удобная песочница, но в мире есть сотни тысяч JS-библиотек и десятки тысяч из них либо написаны на TS, либо имеют выделенные типы.
А единой документации по этим типам нет!
Точнее, не было, но теперь вышел https://tsdocs.dev/
Это система поиска по существующим пакетам с типами. Она установит нужный пакет, распарсит типы и JSDoc и отобразит в удобном формате.
К слову, котаны, что вы предпочитаете по cmd- (ctrl)-click в редакторе? Прыгать к имплементации, или к типам?
Я вот — к имплементации, прыгать к типам меня раздражает.
#typescript #dts #types #doc
👍17👎1
#тип дня
Попробую вместо тега #фишка внести что-то новое для TypeScript. Пусть будет "тип", а там посмотрим.
И сегодня на повестке дня запрет определённых ключей при передаче объекта. Например, контекста в трекинге или тех же пропсов в React.
Как правило, контекст в трекинге передаётся в функцию-хелпер, а после — дополняется какими-то переменными среды. Как-то так:
Понятное дело, нам совсем неохота, чтобы кто-то случайно передал в контекст type, userId или env и затёр всё нафиг.
"Погоди, так разверни контекст повыше", — скажет кто-то умный.
Ну так тоже не стоит, мы не хотим сюрпризов уже на разбореполётов логов в случае инцидента, когда ожидалось одно, а на выходе — другое.
Проще сразу не дать сделать странное, как минимум на этапе введения контекста. Наш вариант в итоге стал таким:
Ну и конечно, ссылка на песочницу: пуньк.
Есть предложения получше, котаны? Ну кроме проверки в рантайме :)
#typescript #ts #type
Попробую вместо тега #фишка внести что-то новое для TypeScript. Пусть будет "тип", а там посмотрим.
И сегодня на повестке дня запрет определённых ключей при передаче объекта. Например, контекста в трекинге или тех же пропсов в React.
Как правило, контекст в трекинге передаётся в функцию-хелпер, а после — дополняется какими-то переменными среды. Как-то так:
function logEvent(severity: LogSeverity = "info", context: LogContext = {}) {
log(severity, {
type: "event",
userId: "[email protected]",
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
#тип дня
Типом дня назначается вон тот в кожаной куртке. Да-да, о тебе говорю!
TL;DR: вычисленный тип функции с дженерик-аргументом можно сузить, декларируя тип как const.
Кроме шуток, разве тебя не бесит, что указываешь вот дженерик тип аргумента функции, производишь манипуляции над переданным объектом — а в ответ тебе вычисленный базовый тип?
Непонятно? Давай так:
Какой вычисленный тип получат first и second?
Очевидно, такой:
Но... это же довольно неудобно. А если мы схему валидируем, например? Или форму описываем. Или тесты пишем в конце-концов. Почему при заранее известном константном типе входных данных мы получаем вычисленную строку?
К счастью, начиная с TypeScript 5.0 можно объявлять дженерики как константные типы! Иначе говоря, работать с максимально узкими (narrow) типами. Это, собственно, аналог указания as const у предопределённой константы.
Использование простое:
И на выходе тип у first и second будет такой:
Песочница: пуньк.
Давайте исходя из полученной информации создадим валидатор по известной схеме данных и дальше поработаем с ним:
Определение getSurname приобретёт такой вид:
То есть, обратите внимание, не просто строку на вход, а конкретные, существующие в нужных параметрах строки.
Теперь это вполне себе напоминает удобную работу с коллекциями данных, не стыдно и форму провалидировать.
Песочница: пуньк.
Штука относительно новая, к ней ещё придётся немного привыкнуть. Но уже можно пробовать применять.
Ах да, ссылка на соответствующий PR в TypeScript: https://github.com/microsoft/TypeScript/pull/51865
#typescript #generic #const
Типом дня назначается вон тот в кожаной куртке. Да-да, о тебе говорю!
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
GitHub
`const` modifier on type parameters by ahejlsberg · Pull Request #51865 · microsoft/TypeScript
With this PR we implement a new const modifier for type parameters. In a function, method, or constructor invocation, when a literal expression in an argument is contextually typed by a const type ...
👍25❤3
#тип дня
Кричащий банан 🍌 (да-да) принёс шикарную фишку TypeScript: как типизировать ключ объекта без каста.
Решение: заранее объявите тип переменной перебора как
Пруф на TS Playground.
#typescript #cast #бородач
Кричащий банан 🍌 (да-да) принёс шикарную фишку TypeScript: как типизировать ключ объекта без каста.
Решение: заранее объявите тип переменной перебора как
keyof typeof. Всё, вы великолепны.Пруф на TS Playground.
#typescript #cast #бородач
👍22❤4
#тип дня
Говоришь такой коллеге: «Ты зачем столько классов насоздавал для такой простой вещи? Используй CSS-переменные».
А он приходит к тебе с картинкой выше и грустный весь.
Что мы делаем в таком случае?
Да очень просто, дополняем интерфейс CSSProperties, чтобы до реакта, наконец, дошло:
Пруф: песочница.
Странно, конечно, что это не из коробки всё.
#css #types #react #typescript
Говоришь такой коллеге: «Ты зачем столько классов насоздавал для такой простой вещи? Используй CSS-переменные».
А он приходит к тебе с картинкой выше и грустный весь.
Что мы делаем в таком случае?
Да очень просто, дополняем интерфейс CSSProperties, чтобы до реакта, наконец, дошло:
declare module 'react' {
interface CSSProperties {
[key: `--${string}`]: string | number
}
}
Пруф: песочница.
Странно, конечно, что это не из коробки всё.
#css #types #react #typescript
👍19❤3
#тип дня
Вышел TypeScript 5.4.
А это что значит? Что у нас теперь есть служебный тип NoInfer.
Коротко, зачем он нужен: с его указанием TS больше не будет пытаться угадать тип передаваемого аргумента. Например, если не указать тип массива
Чтобы такого не происходило, есть два пути. Первый:
Ну такое, многословно и D больше нигде в коде не используется. Вот тут и приходит на помощь NoInfer:
Всё, тип массива определён как, практически, у константы ["red", "yellow", "green"] и передать "blue" уже не выйдет.
Естественно, улучшений и нововведений в TS 5.4 сильно больше, рекомендую прочитать заметки о выпуске: https://devblogs.microsoft.com/typescript/announcing-typescript-5-4/
#typescript #generics
Вышел TypeScript 5.4.
А это что значит? Что у нас теперь есть служебный тип NoInfer.
Коротко, зачем он нужен: с его указанием TS больше не будет пытаться угадать тип передаваемого аргумента. Например, если не указать тип массива
["red", "yellow", "green"], TS определит его как string[] и разрешит запихнуть туда значение "blue" или, что хуже, позволить вашему коду попытаться, например, это самое значение в нём найти:
function createStreetLight<C extends string>(colors: C[], defaultColor?: C) {
// ...
}
createStreetLight(["red", "yellow", "green"], "blue");
Чтобы такого не происходило, есть два пути. Первый:
function createStreetLight<C extends string, D extends C>(colors: C[], defaultColor?: D) {
}
createStreetLight(["red", "yellow", "green"], "blue");
// ~~~~~~
// error!
// Argument of type '"blue"' is not assignable to parameter of type '"red" | "yellow" | "green" | undefined'.
Ну такое, многословно и D больше нигде в коде не используется. Вот тут и приходит на помощь NoInfer:
function createStreetLight<C extends string>(colors: C[], defaultColor?: NoInfer<C>) {
// ...
}
createStreetLight(["red", "yellow", "green"], "blue");
// ~~~~~~
// error!
// Argument of type '"blue"' is not assignable to parameter of type '"red" | "yellow" | "green" | undefined'.
Всё, тип массива определён как, практически, у константы ["red", "yellow", "green"] и передать "blue" уже не выйдет.
Естественно, улучшений и нововведений в TS 5.4 сильно больше, рекомендую прочитать заметки о выпуске: https://devblogs.microsoft.com/typescript/announcing-typescript-5-4/
#typescript #generics
👍20❤4
#ссылка дня
Типизирование React-компонентов, рефов и хуков может обернуться большой болью. Особенно если в первый раз. Но к счастью, у нас есть GitHub, который добрые люди используют не только как хранилище кода, но и как идеальный универсальный блог :)
Встречайте: https://github.com/typescript-cheatsheets/react
Буквально, из описания: «Cheatsheets for experienced React developers getting started with TypeScript».
Впрочем, у ребят есть и более привычная веб-версия, разбитая на секции: https://react-typescript-cheatsheet.netlify.app/
Мне, например, недавно понадобилось сделать полиморфичный компонент, который в зависимости от условий мог становиться то ссылкой, то кнопкой (какой прекрасный повод для холивара). Я не нашёл решения непосредственно здесь, зато нашёл в обсуждениях и PR. Что тоже показывает, как удобен GitHub для коллективного блога.
В общем, всем типов, котаны.
#typescript #react #бородач
Типизирование React-компонентов, рефов и хуков может обернуться большой болью. Особенно если в первый раз. Но к счастью, у нас есть GitHub, который добрые люди используют не только как хранилище кода, но и как идеальный универсальный блог :)
Встречайте: https://github.com/typescript-cheatsheets/react
Буквально, из описания: «Cheatsheets for experienced React developers getting started with TypeScript».
Впрочем, у ребят есть и более привычная веб-версия, разбитая на секции: https://react-typescript-cheatsheet.netlify.app/
Мне, например, недавно понадобилось сделать полиморфичный компонент, который в зависимости от условий мог становиться то ссылкой, то кнопкой (какой прекрасный повод для холивара). Я не нашёл решения непосредственно здесь, зато нашёл в обсуждениях и PR. Что тоже показывает, как удобен GitHub для коллективного блога.
В общем, всем типов, котаны.
#typescript #react #бородач
👍17❤2
#библиотека дня
— А что, если типы надо проверять в рантайме?
— Да не, херня какая-то...
Впрочем, создателям библиотеки Typia так совсем не кажется. Да и вам, в целом, казаться не должно: TypeScript, конечно, большой молодец, но типы проверяет только на этапе компиляции. И, конечно же, мы все верим в святые контракты :)
Библиотека предлагает не только проверку входящих данных, но и соответствие JSON описанию.
Есть как простые is и equals, возвращающие логическое соответствие, так и assert и assertEquals, кидающие ошибку.
Отличие is от equals в том, что is менее строгий и позволяет расширение объекта свойствами, которых нет в интерфейсе. Но описанные при этом должны соответствовать.
Обратили внимание? Схема не нужна!
Как-то так. Кто использовал уже?
#typescript #types
— А что, если типы надо проверять в рантайме?
— Да не, херня какая-то...
Впрочем, создателям библиотеки Typia так совсем не кажется. Да и вам, в целом, казаться не должно: TypeScript, конечно, большой молодец, но типы проверяет только на этапе компиляции. И, конечно же, мы все верим в святые контракты :)
Библиотека предлагает не только проверку входящих данных, но и соответствие JSON описанию.
Есть как простые is и equals, возвращающие логическое соответствие, так и assert и assertEquals, кидающие ошибку.
Отличие is от equals в том, что is менее строгий и позволяет расширение объекта свойствами, которых нет в интерфейсе. Но описанные при этом должны соответствовать.
const input: unknown = {
id: v4(),
email: "[email protected]",
age: 30,
extra: "superfluous property", // extra
};
const is: boolean = typia.is<IMember>(input);const
equals: boolean = typia.equals<IMember>(input);
console.log(is, equals); // true, falseОбратили внимание? Схема не нужна!
Как-то так. Кто использовал уже?
#typescript #types
🤩4
#инструмент дня
Если меня спросят, на чем я стал бы делать ранний прототип, я бы ответил Drupal. Ну или Ruby on Rails. Ну просто потому что я их знаю. На Drupal вообще мышкой можно все накликать и получить рабочий API.
Но, конечно, это уже сравнительно оторвано от реальности :) Простой CRUD aka Create-Read-Update-Delete можно собрать тысячей разных способов.
Так что стоит принести ещё один: Remult.
https://remult.dev/
Remult использует всю мощь декораторов TypeScript для описания своих т. н. сущностей, которые потом станут моделями.
После чего остается только зарегистрировать созданную сущность в сервере на, например, express и получить готовый CRUD API.
Останется дело за фронтендом, который автоматом тут не генерируется, зато вам доступно множество хелперов для управления своими сущностями.
Прекрасная документация, отличные пошаговые инструкции к разным фреймворкам. Ребята однозначно старались.
Горячая рекомендация, в целом.
#typescript #crud
Если меня спросят, на чем я стал бы делать ранний прототип, я бы ответил Drupal. Ну или Ruby on Rails. Ну просто потому что я их знаю. На Drupal вообще мышкой можно все накликать и получить рабочий API.
Но, конечно, это уже сравнительно оторвано от реальности :) Простой CRUD aka Create-Read-Update-Delete можно собрать тысячей разных способов.
Так что стоит принести ещё один: Remult.
https://remult.dev/
Remult использует всю мощь декораторов TypeScript для описания своих т. н. сущностей, которые потом станут моделями.
import { Entity, Fields } from "remult"
@Entity("tasks", {
allowApiCrud: true
})
export class Task {
@Fields.cuid()
id = ""
@Fields.string()
title = ""
@Fields.boolean()
completed = false
@Fields.createdAt()
createdAt?: Date
}После чего остается только зарегистрировать созданную сущность в сервере на, например, express и получить готовый CRUD API.
Останется дело за фронтендом, который автоматом тут не генерируется, зато вам доступно множество хелперов для управления своими сущностями.
Прекрасная документация, отличные пошаговые инструкции к разным фреймворкам. Ребята однозначно старались.
Горячая рекомендация, в целом.
#typescript #crud
👍20