Frontend Alliance | Автушенко Андрей
2.22K subscribers
101 photos
3 videos
54 links
Меня зовут Автушенко Андрей. Здесь рассказываю про карьеру во frontend-разработке, менторство и личную жизнь

Менторство - frontend-alliance.ru
Отзывы - https://t.me/palaxius_reviews
Написать лично - @palaxius
YouTube: youtube.com/@FrontendAlliance
Download Telegram
Итоги 2025 года

Друзья, всех с наступающим Новым годом! 🎄
Этот год вышел очень насыщенным на работу и активности. Хочу закрепить основные.

— Сообщество Frontend Alliance практически дошло до 200 участников.
Это безумно мотивирует. Особенно радует, когда ребята разбираются в новом материале, растут и получают офферы.
Мы, в свою очередь, постоянно улучшаем программу и добавляем новые активности.

— Запустили свой сайт.

— Сделали бесплатный frontend роудмап.

— Запустили YouTube.
К сожалению, на него совсем не было времени и сил. Это точно один из ключевых векторов развития на следующий год.

— Даже успели запустить свой первый мерч!)

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

Сделал предложение своей возлюбленной 💍
И ещё успел побывать на свадьбе Димы (вдохновился).

— Записал два технических курса (не на свой канал), которые выйдут примерно в феврале–марте следующего года. По System Design и TypeScript.
В следующем году хочу еще больше прокачаться в этом ремесле.

— Начал ходить в зал и целый год стабильно его посещал.
Одно из лучших решений года: набрал 12 кг, почти пожал сотку, чувствую себя прекрасно.

— Этот канал практически дорос до подписчиков, чему я безумно рад.

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

Спасибо, что читаете и поддерживаете!
🎄С наступающим новым годом!
Искренне желаю, чтобы 2026 стал стартом чего-то большего в вашей жизни!

✈️ Telegram | 🎓 Менторство | 📹 YouTube | 👩‍💻 Roadmap
Please open Telegram to view this post
VIEW IN TELEGRAM
1🔥2513🎉10👍2🥰1
Разница между type и interface в TypeScript

В TS есть два способа описать структуру данных - type и interface.
На первый взгляд они похожи: оба позволяют объявлять объекты, описывать сигнатуры функций и имплементацию классов.
Но есть важные отличия, которые стоит понимать.

В современном TS типы и интерфейсы практически взаимозаменяемые. Но тип может использоваться для примитивов, а интерфейс нет (всегда объектный тип).

В простых случаях интерфейс и тип почти одно и то же (скрин 1).

И TUser, и IUser описывают одинаковую структуру объекта.
Чтобы объединить несколько типов: для интерфейсов используется extends, а для типов - пересечение (&).

При этом TypeScript рекомендует использовать интерфейсы. Почему так?

Есть несколько причин.

1️⃣ Производительность и проверки типов:

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

2️⃣Как TypeScript проверяет совместимость.
Когда мы сравниваем объект с типом A & B - TS сначала проверяет совместимость с A, потом с B
При interface extends - TS работает с единым структурным описанием типа.

3️⃣Отсюда третий момент - ошибки.

У интерфейсов ошибки обычно короче и понятнее: TS сразу показывает все конфликтующие поля.
Через type - вычисляет пошагово и сначала сообщит, что не хватает одного ключа, а уже потом другого.

В примере (скрин 2) ошибка при использования type, говорит, что отсутствует поле agе, но не говорит про поле address, которое тоже отсутствует.

4️⃣Интерфейсы поддерживают объединение объявлений (declaration merging).

Если объявить интерфейс с тем же именем, что и уже существующий, TS объединит их свойства.

Это может показаться небезопасным.
Однако у этого есть важный плюс: часто необходимо расширять уже существующие интерфейсы, например:
- Добавлять новые свойства к браузерным событиям.
- Расширять глобальные объекты, например Window, если внешняя SDK добавляет на него свой инстанс класса (скрин 3).

В таких случаях declaration merging позволяет TS понимать новые свойства и типизация при этом остается корректной. С type так делать нельзя.

Я в проектах предпочитаю везде использовать интерфейсы, а типы только для примитивов.

✈️ Telegram | 🎓 Менторство | 📹 YouTube | 👩‍💻 Roadmap
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
1🔥28👍975😁1🤣1
Статическая и динамическая типизация

В JavaScript есть типы. Существует 8 типов данных.
Но при этом JS имеет динамическую и неявную типизацию.

🟣Динамическая означает, что тип переменной определяется не при ее объявлении, а во время выполнения программы в момент присваивания значения и может меняться динамически.

let value = 42;      // Тип: number
console.log(typeof value); // "number"

value = "текст"; // Тип изменился во время выполнения
console.log(typeof value); // "string"

value = true; // Тип снова изменился
console.log(typeof value); // "boolean"


🟣Неявная говорит о том, что тип выводится автоматически без явного указания.

let x = 'hello world!'
console.log(typeof x); // "string"

В данной строке javascript автоматически определит, что в переменной x лежит строка.

TypeScript также поддерживает неявный вывод типов, который помогает упростить код.
Однако не всегда можно полностью полагаться на него - в некоторых случаях компилятор не сможет однозначно определить тип.

При необходимости в TypeScript можно явно указать тип для переменных, чтобы повысить читаемость и надежность кода:
let x: string = 'hello world!'


🟣TypeScript является надстройкой над JavaScript и добавляет статическую типизацию.

Это означает, что тип переменной определяется на этапе компиляции кода, что позволяет получать мощные подсказки от IDE и выявлять ошибки на этапе написания кода.
То есть код анализируется не запускаясь.

🟣Это круто, но при этом имеет свои ограничения, например, при работе с данными в райнтайме.

Если мы делаем fetch-запрос к бэкенду и ожидаем данные определённого типа - TS вынужден «доверять» разработчикам, потому что не сможет проверить этот тип во время выполнения.

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

type Mentor = {
id: string;
name: string;
email: string;
};

async function fetchMentor(id: number): Promise<Mentor> {
const res = await fetch(`/api/mentors/${id}`);
return await res.json(); // ← TypeScript верит, что это тип Mentor
}

const mentor = await fetchMentor(1);

// А с сервера пришло { "id": 1, "fullname": "Frontend Alliance" }

console.log(mentor.name.toUpperCase()); // Упали в рантайме, так как нет поля name


TypeScript проверяет типы только во время компиляции, а не в рантайме. 

Тоже самое касается например парсинга localStorage - мы не можем в райтайме проверить, что там в действительности будет.

const x: number = JSON.parse(localStorage.getItem('my-favorite-number') || '');


🟣Когда мы видим запись вида:
const x: number


мы думаем, что в переменной x лежит число, но это не до конца верно.

Эта запись означает, что мы верим, что там лежит число, но формально этого может не быть.

На этапе статического анализа мы не можем гарантировать, что после fetch'a и LS'a данные будут определенного типа.

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

✈️ Telegram | 🎓 Менторство | 📹 YouTube | 👩‍💻 Roadmap
Please open Telegram to view this post
VIEW IN TELEGRAM
1👍15🔥1461
Структурная и номинальная типизация

🟣 Номинальная типизация (nominal typing) - это когда совместимость типов определяется по их имени или идентификаторам, а не по внутренней структуре.
Даже если структура одинаковая - типы разные.
Два типа считаются совместимыми, только если они явно связаны иерархией.

Это более строгий и безопасный, но менее гибкий подход по сравнению со структурной типизацией, характерный для языков типа Java и C#.

Рассмотрим пример кода на Java (скрин 1).
У нас есть класс User и класс Admin. И если мы попытаемся создать переменную user типа User и присвоить в нее инстанс класса Admin, то будет ошибка, потому что в джаве номинальная типизация и фактически эти типы разные.

🟣Структурная типизация (structural typing) - это система проверки типов, где совместимость определяется не по имени, а по внутренней структуре.

TypeScript как раз использует структурную типизацию, то есть:

Совместимость типов определяется по структуре - если объект имеет все требуемые свойства и методы, он считается совместимым, независимо от имени типа.

Тот же самый пример (скрин 2).
TypeScript разрешает это присваивание, потому что у обоих типов одна и та же структура (свойство name типа string).

🟣Но мы можем заставить типы в классах вести себя как номинальные, для это нужно добавить приватное поле (скрин 3).

Когда мы объявляем поле с ключевым словом private, то TS применяет ограничение на уровне компиляции. Это означает, что приватное поле может быть доступно только внутри того же класса.

Поэтому два класса, которые имеют приватные поля с одинаковыми именами - структурно несовместимы, так как тайпскрипт будет учитывать "происхождение" приватных полей.

При необходимости номинальное поведение так же можно симулировать через брендирование (расскажу в отдельном посте).

🟣Структурная типизация хоть и считается "гибкой", но может привести к неожиданным проблемам. Рассмотрим следующий пример (скрин 4):

У нас есть тип DatabaseConfig, который имеет обязательное поле url и необязательное поля retries. И тип APIConfig, который также имеет обязательное поле url и необязательно поле timeout.
Есть функция startApi, которая принимает конфиг, который должен быть типа APIConfig.
Но из-за структурной типизации мы можем передать внутрь функции переменную, которая имеет другой тип, так как их структура типов совпала.
ТС это разрешил, но в реальности внутри функции мы не сможем обращаться к полю retries - будет ошибка.

В реальности подобные проблемы можно ловить через сотни строчек кода и десятки файлов. Поэтому я считаю, что наличие структурной типизации в ТС - это большое допущение, которое приходится фиксить дополнительными танцами с бубном ради безопасной системы типов.

✈️ Telegram | 🎓 Менторство | 📹 YouTube | 👩‍💻 Roadmap
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥25👍873
Брендирование (branding) в TypeScript

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

У нас есть интерфейс заказа Order и интерфейс карты пользователя UserCard. Есть функция proceedOrders, которая валидирует заказы и возвращает только успешные.

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

Но из-за того, что TypeScript использует структурную типизацию, два типа считаются одинаковыми, если у них одинаковая структура. Это значит, что мы легко можем приравнять обычный заказ к успешному и потерять важный кусок информации.
Мы можем обезопасить себя с помощью брендирования.

🟣Брендирование - это искусственное примешивание поля в тип, чтобы сделать его более специфичным и уникальным и предотвратить неправильные присваивания.

Мы добавляем в интерфейс успешный заказ дополнительное поле brand и так же внутрь функции, так как ТС будет от нас ожидать этого поля (скрин 2).

После этого TypeScript начинает различать обычный и успешный заказ на уровне типов.

🟣Но даже при таком подходе остаётся проблема: поле brand можно подделать вручную, и проверку получится обойти.
Более надежный вариант — использовать уникальные символы (скрин 3).

В брендировании через символы мы определяем бренд как уникальный символ и описываем поле через typeof. Затем внутри функции присваиваем это брендированное поле.

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

🟣Мы также можем написать вспомогательный дженерик, чтобы удобно создавать брендированные типы.

type Branded<T extends { id: string }> = T & {
id: T['id'] & { __brand: T }
}

type User = Branded<{
id: string,
email: string
}>

type Wallet = Branded<{
id: string
amount: bigint
}>


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

🟣Резюмируя: брендирование - это способ повысить надежность типов в условиях структурной типизации TypeScript, гарантируя, что важные различия между типами не будут случайно потеряны.

✈️ Telegram | 🎓 Менторство | 📹 YouTube | 👩‍💻 Roadmap
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥206👍31