xycu2.rar
51 subscribers
116 photos
8 videos
14 links
Download Telegram
Пока менюшка выглядит вот так:

Эти утилиты уже работают, и их можно использовать!
Media is too big
VIEW IN TELEGRAM
Добавил подтверждение перед push и init, чтобы случайно ничего не запустить по ошибке. Показываю на видео, как теперь это работает!
Добавил создание веток в свой git-helper.bat 🚀

Продолжаю допиливать свой батник для автоматизации работы с Git. Вчера написал метку для создания новой ветки с автоматическим переключением на неё (git switch -c).

Что внутри метки :create_branch:
• Подтверждение действия через меню (choice);
• Проверка на пустое имя ветки (чтобы скрипт не падал с ошибкой);
• Мгновенный переход в новую ветку и возврат в главное меню.

Код функции:
:: Создание ветки и сразу переходим в нее
:create_branch
cls
echo === СОЗДАНИЕ ВЕТКИ (ПЕРЕКЛЮЧАЕМСЯ НА НЕЕ) ===

echo.
echo Создать новую ветку и перейти в нее? (Y/N)

choice /c YN /n /m "Ваш выбор: "

if errorLevel 2 goto menu

set branch_name=
set /p branch_name="Введите название новой ветки: "

if "%branch_name%"=="" (
echo [!] Имя ветки не может быть пустым!
pause
goto menu
)

git switch -c "%branch_name%"

echo.
echo [!] Вы успешно перешли в созданную ветку
pause
goto menu


Мелочь, а рутину и консольные команды сильно сокращает. Постепенно собирается полноценное CLI-меню для удобной разработки 💻
Добавил создание ветки БЕЗ перехода

Вдобавок к прошлой функции сделал метку для случая, когда ветку нужно просто создать в фоне, оставаясь в текущей (git branch).

Что нового в :create_branch_only:
• Вывод подсказки с нужной командой для перехода на будущее.

Код функции:
:: Создание ветки (остаемся в текущей)
:create_branch_only
cls
echo === СОЗДАНИЕ ВЕТКИ (ОСТАЕМСЯ В ТЕКУЩЕЙ) ===

echo.
echo Вы хотите создать ветку и остаться в текущей? (Y/N)

choice /c YN /n /m "Ваш выбор: "

if errorLevel 2 goto menu

set branch_name=
set /p branch_name="Введите название новой ветки: "

if "%branch_name%"=="" (
echo [!] Название ветки не может быть пустым!
pause
goto menu
)

git branch "%branch_name%"

echo.
echo [!] Ветка "%branch_name%" успешно создана! Вы остались в текущей ветке.
echo Чтобы перейти на нее выполните: git switch "%branch_name%"
pause
goto menu


Теперь в скрипте закрыты оба базовых сценария работы с ветками 💻
🔥 Сегодня разобрал крутую фичу в TypeScript оператор satisfies (появился в TS 4.9).

Раньше была частая проблема: если задаёшь объекту строгий тип (например, Record), то TypeScript забывает, где у тебя строка, а где массив. Из-за этого нельзя было просто так вызвать .toUpperCase() у строки без лишних проверок.

Оператор satisfies решает это идеально:
1. Он ПРОВЕРЯЕТ, что объект соответствует типу.
2. Но НЕ СУЖАЕТ автовыведенные типы полей!

type Colors = "red" | "green" | "blue"
type RGB = [red: number, green: number, blue: number]

const palette = {
red: [255, 0, 0],
green: "#00ff00",
blue: [0, 0, 255],
} satisfies Record<Colors, string | RGB>

const greenNormalized = palette.green.toUpperCase()


Теперь TS точно знает, что palette.green это именно string, и спокойно даёт крутить с ним любые строковые методы.

Код и примеры закинул в конспекты, двигаемся дальше 🚀
Разбор задачи: пишем свой DeepPartial в TypeScript 🧠

Встроенный тип Partial делает необязательными только ключи верхнего уровня. А что делать, если у нас сложная вложенная структура и мы хотим сделать optional абсолютно все поля на всех уровнях вложенности?

Пишем свой рекурсивный тип!

type DeepPartial<T> = {
[key in keyof T]?: T[key] extends Record<string, unknown>
? DeepPartial<T[key]>
: T[key]
}


Что тут происходит и какие фишки TS задействованы:
1) Mapped Types ([key in keyof T]?: ...) - проходим по всем ключам объекта и делаем их необязательными с помощью ?.
2) Conditional Types (T[key] extends ... ? A : B) - проверяем, является ли значение вложенным объектом.
3) Record - безопасная проверка на объектную структуру (в отличие от any).
4) Recursive Types (DeepPartial) - если внутри объект, вызываем DeepPartial снова для вложенного типа.

💻 Пример использования:
type User = {
name: string;
details: {
age: number;
address: {
city: string;
};
};
};

// Теперь ВСЕ поля на любом уровне вложенности стали необязательными!
type PartialUser = DeepPartial;

const user1: PartialUser = {}; // Валидно

const user2: PartialUser = {
details: {
address: {
city: "Москва", // Не обязательно передавать name или age
},
},
};
Автоматизирую Git: утилита для слияния веток ⚙️

Решил упростить себе жизнь и написать тулзу (merger_branch.bat), которая берет на себя рутину при работе с feature-ветками.
Что уже работает:

1. Автоматическое определение текущей ветки.
2. Проверка изменений в проекте через git status --porcelain.

Что в планах:
Добавить автоматический пуш фичи, быстрый перенос изменений в main и забор обновления с GitHub.
Маленькими шагами собираем полезную автоматизацию 💻✨
🔥1
Помните шаблон скрипта, который я выкладывал пару дней назад?

Написал для него полноценную логику! Теперь вся рутина с ветками автоматизирована:
add ➔ commit ➔ push feature ➔ checkout main ➔ pull ➔ merge ➔ push main.
Осталось добавить пару мелких фишек, и будет идеальная утилита для ежедневной работы!
Решил сегодня разобраться с MJML для верстки писем 👨‍💻

Набросал шапку, превью обновляется прямо в VS Code. Фреймворк максимально экономит время, никаких бесконечных таблиц и костылей под почтовые клиенты.

Доверстаю письмо до конца и покажу результат!
❤2
Media is too big
VIEW IN TELEGRAM
Полностью доделал письмо на MJML! ✉️

Сверстал его всего за пару часов. Итоговый HTML-файл получился на 1500+ строк кода, хотя исходник на MJML занял всего около 160 строк. Вот так фреймворк реально облегчает работу с адаптивной версткой писем! 🚀
❤2
Начинается самое интересное в TypeScript:
// .ts, .dts -> skipLibCheck
// Где писать типы? .ts / dts
// import из dts без импорта
// dts за пределами src -> includes
// reference type, reference path
// npm lodash (посмотреть кол-во js'ов) .dts, vite .ts (@types)
// lodash внутри и как там устроен reference

// что такое declare
// Как типизировать window

// declare module 'libname'
// "paths": { "some-library": ["./src/two.ts"] },

// типизация картинок(svg/png/jpg), css

Переходим к .d.ts, declare и модулям!
Сегодня наконец-то до конца понял, зачем нужны декларативные файлы (.d.ts) в TypeScript! 💡

Раньше эта тема казалась какой-то абстрактной теорией, но сегодня наткнулся на шикарный пример с проверкой на исчерпываемость (assertExhaustiveness).
В чем суть:
1) Объявляем сигнатуру функции function assertExhaustiveness(value: never): never в .d.ts файле.
2) Теперь TS во время разработки нативно подхватывает её и сужает типы! Если я забыл обработать какой-то из статусов в if, TypeScript сразу подсветит ошибку и не даст собрать проект.
3) При этом сама инструкция работает только как страховка на этапе компиляции и исчезает в итоговом JS.

Очень крутое ощущение, когда непонятная концепция превращается в логичный и понятный инструмент. Буду изучать дальше, чтобы еще лучше разбираться в конфигах и продвинутой типизации!
❤1
Продолжаю прокачивать TypeScript и сегодня плотно погрузился в тему infer 🧠

Суть проста: infer позволяет TypeScript вытащить конкретный тип из сложной конструкции (например, из аргументов или возвращаемого значения функции).
Сделал небольшой дженерик MyParams, который достаёт тип аргумента id:
function getUser(id: number) {}
function getUser2(id: string) {}

type MyParams<T> = T extends ((id: infer R) => unknown) ? R : never;

type Test1 = MyParams<typeof getUser> // number
type Test2 = MyParams<typeof getUser2> // string


💡 Интересный нюанс, который сегодня осознал: Параметры любой функции в TS на уровне типов всегда представляются в виде массива (кортежа).

Например, для (x: number, y: string) => string параметры на уровне типов — это [number, string].

Именно поэтому запись (...args: any[]) => any универсальна и подходит под функцию с любым количеством аргументов (от 0 и больше), ведь их параметры просто упакованы в массив!
Двигаемся дальше 🔥
1❤2
Вечерний кодинг 🧑‍💻

Сел заниматься TypeScript! Cегодня разбираю ООП, типизацию классов и модификаторы доступа (public, protected, private, readonly).

На скрине как раз пример с protected , и кайф в том, что свойство можно использовать в дочерних классах (наследовать логику), но снаружи класса к нему уже не достучаться. После чистого JS такие штуки делают архитектуру кода намного надёжнее и понятнее.

Медленно, но верно закрываю темы по TS одна за другой.

А как у вас вечер проходит? Чем занимаетесь? 👇
❤2
Разобрался с перегрузками конструкторов в TypeScript ⚡️

В классах это работает так же, как и для обычных функций: пишем сигнатуры для разных вариантов вызова сверху, а всю логику реализуем в одном универсальном конструкторе снизу.
class Video {
name: string = '';
length: number = 0;

// Сигнатуры перегрузок
constructor(name: string)
constructor(name: string, description: string, length: number)

// Тело конструктора (реализация)
constructor(name: string, description?: string, length?: number) {
this.name = name;
this.length = length || 60;
}
}

// Так можно:
new Video('js');
new Video('js', 'описание', 10);

// А вот так TS не даст сделать, так как сценарий не описан:
// new Video('js', 'описание');

export {}


Очень удобная штука для строгой типизации, когда класс может создаваться с разным набором аргументов.
Что там по моей BAT-утилите?

Не трогал её недели 3, но как раз 3 недели назад полностью дописал логику для автоматического слияния веток (Merge Flow).

Правда, в основной файл меню ещё не успел её интегрировать!
Вся рутина с Git (add, commit, push, переключение и вливание веток в main) теперь сводится к нажатию пары цифр в терминале.

Сейчас хочу довести утилиту до ума. Подскажите, что ещё полезного стоит в неё добавить?

Кидайте свои идеи в комментарии, попробую реализовать! ))
Начал практиковать типизацию классов.

Набросал логику , как решать эту задачу. Буду переносить это в код! 🧑‍💻
Решил задачу:
abstract class BaseVideo {
constructor(
public ID: string,
public Title: string,
public Duration: number
) {}

getInfo() {
return `ID: ${this.ID}, Title: ${this.Title}, Duration: ${this.Duration}`
}

abstract play(): void
}

class YouTubeVideo extends BaseVideo {
addToPlaylist(playlistName: string) {
console.log(`Добавлен в плейлист ${playlistName}`)
}

play() {
console.log(`Запускаю длинное видео ${this.Title} ...`)
}
}

class Shorts extends BaseVideo {
constructor(ID: string, Title: string, Duration: number) {
if (Duration > 60) {
throw new Error("Ошибка: превышение лимита длительности")
}
super(ID, Title, Duration)
}

play() {
console.log(`Запускаю шортс ${this.Title} ...`)
}
}

Задача была интересная , проблемы возникли с проверкой длины шортса, но я разобрался 🥳