progway — программирование, IT
2.6K subscribers
25 photos
1 video
246 links
Чат: @prog_way_chat

Разборы вопросов и задач с собеседований, мысли, полезные материалы и просто вещи, что мне интересны из мира IT

Полезности и навигация в закрепе

По всем вопросам: @denisputnov
Download Telegram
Сужение типов и уточняющие тайпгарды

Часто в TypeScript коде можно столкнуться с тем, что типы недостаточно точны. Это бывает в случаях, когда мы определяем типы, например, через unionstring | number и других подобных случаях. Рассмотрим пример:

const value: string | number = ...

parseInt(value) // ошибка


В таком случае мы получим TypeError , а всё потому что переменная value имеет более общий тип, чем тот, что ожидает parseInt — эта функция ожидает первым аргументом на вход только строку. В таком случае нам может помочь сужение типов, которое нам дают дополнительные проверки — тайпгарды.

Тайпгарды — Type Guards — это языковые конструкции, проверки, которые позволяют определить или уточнить тип переменной средствами JavaScript. Тайпгарды бывают двух видов — уточняющие и определяющие.

Уточняющие тайпгарды — проверки, которые позволяют вывести более узкий тип из общего типа, например вывести string из типа string | number | boolean | null.

Определяющие же тайпгарды — проверки, которые позволяют вывести узкий тип из неизвестного типа, который в идеальном мире обозначается типом unknown

Есть несколько способов сузить тип:

1. Через оператор typeof

const value: string | number = ...

if (typeof value === 'string') {
// ошибки не будет, т.к.
// тип value в этом условии
// строго равняется "string"
parseInt(value)
}


2. Через ключевое слово in или метод hasOwnProperty

const value: any[] | number = ...

if ("map" in value) {
// value === array
value.map(...)
}

const animal = Cat | Dog = ...

if (animal.hasOwnProperty('meow')) {
// animal === Cat
animal.meow()
}


3. Через ключевое слово instanceof

const person: Fireman | Programmer = ...

// при условии что Programmer - класс
// а person - инстанс класса
if (person instanceof Programmer) {
person.code()
}


4. Через встроенные в язык функции проверки типа

const value: any[] | number = ...

if (Array.isArray(value)) {
value.map(...)
}


5. Через existed check, например, для null и undefined

const user: User | null = ...

if (user) {
console.log(user.name)
}


Также подобные проверки можно выносить в отдельные функции, которые обычно именуются по следующему шаблону — is + <название типа или интерфейса> , например, для типа User будет логично создать тайпгард isUser, вот небольшой пример:

type Account = User | Admin | Manager

const isUser = (account: Account): account is User => {
return account.type === "user"
}


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

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

Если более просто, то мы знаем, что если account.type === “user” , то перед нами ни что иное как User. В этом случае тип аккаунта является предикатом, на основе которого можно делать выводы о типе аккаунта без дополнительных проверок.

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

Спасибо за прочтение, это важно для меня ❤️

@prog_way_blogчат#theory #code #typescript
🔥20👍127🐳2🎄1
Определяющие тайпгарды

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

Определяющие тайпгарды — это функции, которые позволяют уточнить тип unknown переменной. Сразу рассмотрим пример и рассмотрим следующий интерфейс:

interface User {
name: string;
age: number;
roles: string[];
}


И представим, что в коде у нас есть некоторая переменная с неизвестным типом, которую мы хотим обработать:

const foo: any = ...

if (isUser(foo)) {
// обработать как пользователя
} else {
// обработать как что-то иное
}


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

function isUser(value: unknown): value is User {
const user = value as User;

return user !== null
&& typeof user === 'object'
&& typeof user.name === 'string'
&& typeof user.age === 'number'
&& Array.isArray(user.roles)
&& user.roles?.every(role => typeof role === 'string');
}


Это может выглядеть очень некрасиво, но главное, что работает)

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

Спасибо за прочтение, это важно для меня ❤️

@prog_way_blogчат#theory #code #typescript
👍175🔥4🤔1🐳1
JavaScript не в браузере: NodeJS, Deno и Bun

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

Рантаймов несчётном множество, как и всего в мире JS, но в этом поговорим о самых интересных: NodeJS, Deno и Bun

👩‍💻 NodeJS — это рантайм JavaScript, построенный на движке V8 от Google Chrome. Он позволяет выполнять JavaScript-код на сервере. По сути, это почти то же самое, что и выполнять код в браузере, за тем лишь исключением, что NodeJS предоставляет API по работе с файлами, операционной системой, среду исполнения тестов и кучу всего ещё

— NodeJS
сейчас — это основной рантайм для всех серверных решений на JavaScript и TypeScript
npm (Node Package Manager) — это тоже про ноду

👩‍💻 Deno — более новый, чем NodeJS, рантайм, от создателя NodeJS. Deno разработан для того, чтобы устранить некоторые недостатки и ограничения ноды, улучшить безопасность и лучшей поддержкой TypeScript. В целом, всё так и есть, только лично мне субъективно кажется, что Deno почти никто не использует(

👩‍💻 Bun — самый новый из этой тройки инструмент, который разработан для того, чтобы быть самым быстрым и легким в использовании, предлагая ряд улучшений по сравнению с другими инструментами. Bun быстрее запускает код, сильно лучше работает с асинхронными запросами, быстрее устанавливает зависимости... Но ему слишком мало лет, чтобы активно тащить его в прод — пока рановато

Краткий итог:

NodeJS - это зрелая платформа с обширной экосистемой и большим сообществом
Deno - альтернатива NodeJS с улучшенной безопасностью, поддержкой TypeScript и другими плюшками
— Bun - новая и перспективная платформа с упором на скорость и интеграцию различных инструментов


Лично я ставлю на то, что доминирующего рантайма пока что не будет, получится зоопарк, так как всем просто не угодить

Спасибо за прочтение, это важно для меня ❤️

@prog_way_blogчат#theory #javascript #typescript #useful
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥22👍1211🐳1
Составные компоненты

Есть такой паттерн для реакта, который называется Compound Components. Это можно перевести как "составные компоненты"

Смысл этого паттерна заключается в том, что мы можем связать компоненты общим окружением и эффективнее использовать их вместе

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


Запутались в словах? Лучше посмотреть в коде:

Вот пример прям из доки Ant Design:
import { Layout } from 'antd';

<Layout>
<Layout.Header>Header</Layout.Header>
<Layout.Content>Content</Layout.Content>
<Layout.Footer>Footer</Layout.Footer>
</Layout>


Обратили внимание, как компоненты Header, Content и Footer мы получаем напрямую из компонента Layout? Это и есть пример паттерна Compound Components. Компоненты связаны, а используются они из общего пространства — компонента Layout

Зачем же так сделали? Тут преследуется три цели:
1. Явно показать на уровне нейминга, что использовать Layout.Footer вне Layout не нужно
2. Расшарить общий контекст между всеми компонентами Layout
3. Корректно стилизовать части Layout в зависимости от значения внутри общего контекста

С неймингом и стилями, думаю, всё предельно ясно. Но что насчёт контекста? На самом деле, Layout под собой содержит ещё и LayoutContext, который содержит в себе состояние компонента Sider и распространяет его на все дочерние компоненты. Схематически это выглядит примерно так:

InternalLayout  
└ LayoutContext <-- инициализируем контекст
├ Header
├ Content <-- а в этих компонентах получаем его значение
├ Footer
└ Sider


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

С точки зрения типов всё тоже не сложно. Тот же Ant Design делает так:
import InternalLayout, { Content, Footer, Header } from './layout';
import Sider from './Sider';

// получаем тип базового layout компонента
type InternalLayoutType = typeof InternalLayout;

// создаём тип, который определит какие компоненты мы вложим в layout
type CompoundedComponent = InternalLayoutType & {
Header: typeof Header;
Footer: typeof Footer;
Content: typeof Content;
Sider: typeof Sider;
};

// нагло переприсваиваем тип
const Layout = InternalLayout as CompoundedComponent;

// нагло биндим нужные компоненты
Layout.Header = Header;
Layout.Footer = Footer;
Layout.Content = Content;
Layout.Sider = Sider;

// не менее нагло экспортируем как public-api
export default Layout;


Спасибо за прочтение, это важно для меня ❤️

@prog_way_blogчат#web #javascript #typescript #theory #code #react #patterns
👍268🔥5🐳3