Что за страшный зверь этот ваш карринг? Говорим о набирающем популярность TypeScript, рассказываем и показываем на нем же.

Освой карринг и шаблоны TypeScript быстро и безболезненно

Ты научишься создавать типы для карринга и Ramda. Для следования этому гайду желателен опыт работы с примитивными типами TypeScript. К концу ты узнаешь, как создавать мощные типы вроде этого:

Освой карринг и шаблоны TypeScript быстро и безболезненно

Карринг или каррирование – это процесс преобразования функции, которая принимает несколько аргументов, в серию функций, которые принимают один аргумент за раз.

Вот функция, которая принимает два числа и возвращает их сумму:

Освой карринг и шаблоны TypeScript быстро и безболезненно

Каррированная версия simpleAdd выглядит так:

Освой карринг и шаблоны TypeScript быстро и безболезненно

В этом руководстве сначала рассмотрим, как создавать типы TypeScript, которые работают со стандартной реализацией карринга.

Затем мы будем развивать их в продвинутые типы, что позволяют каррированным функциям принимать 0 или более аргументов.

Освой карринг и шаблоны TypeScript быстро и безболезненно

Наш первый тип карринга принимает кортеж параметров P и возвращаемый тип R . Это тип рекурсивной функции, который зависит от длины P :

Освой карринг и шаблоны TypeScript быстро и безболезненно

Если HasTail сообщает false – все параметры использованы и пришло время вернуть тип R из оригинальной функции. Иначе, если остаются параметры для использования, мы выполняем рекурсию внутри типа. CurryV0 описывает функцию с возвращаемым типом CurryV0 , пока существует Tail ( HasTail<P> extends true ).

Вот доказательство без какой-либо реализации:

Освой карринг и шаблоны TypeScript быстро и безболезненно

Освой карринг и шаблоны TypeScript быстро и безболезненно

Представим рекурсию выше:

Освой карринг и шаблоны TypeScript быстро и безболезненно

Подсказки типов работают для неограниченного количества параметров:

Освой карринг и шаблоны TypeScript быстро и безболезненно

Мы забыли обработать сценарий, в котором передаются оставшиеся параметры:

Освой карринг и шаблоны TypeScript быстро и безболезненно

Мы попытались использовать оставшиеся параметры, но это не сработает, так как ожидается один параметр или аргумент, который мы назвали arg0 . Так, нужно получить хотя бы один аргумент arg0 и любые дополнительные (необязательные) аргументы внутри оставшегося параметра rest . Включаем остальные параметры с помощью Tail и Partial :

Освой карринг и шаблоны TypeScript быстро и безболезненно

Освой карринг и шаблоны TypeScript быстро и безболезненно

Освой карринг и шаблоны TypeScript быстро и безболезненно

Ужасная ошибка! Аргументы обрабатываются очень плохо, а TS молчит 🙁

Освой карринг и шаблоны TypeScript быстро и безболезненно

Это проблема проектирования, которая возникает из-за единственного принимаемого аргумента arg0 . Нужно отслеживать аргументы, которые используются одновременно. Избавимся от arg0 и начнем отслеживать используемые параметры:

Освой карринг и шаблоны TypeScript быстро и безболезненно

Но теперь мы потеряли проверку типов, потому что указали отслеживание любых [] параметров. Но дело не только в этом. Теперь использование Tail бессмысленно, потому что Tail работает, когда принимается один аргумент за раз.

Нужно больше инструментов!

Рекурсивные типы

Рассмотрим инструменты для определения параметров. Отслеживая используемые параметры с помощью T , мы сможем угадать оставшиеся параметры.

Пристегните ремни! Очередная мощная техника прямо по курсу:

Этот тип принимает кортеж в качестве параметра и извлекает последнюю запись:

Освой карринг и шаблоны TypeScript быстро и безболезненно

Давайте проверим:

Освой карринг и шаблоны TypeScript быстро и безболезненно

Основные инструменты №1

Где мы? Нам нужны инструменты для отслеживания аргументов, помните? А значит, нужно знать, какие типы параметров можно использовать, какие из них были использованы, и какие будут следующими. Приступим!

Length

Для анализа выше нужно выполнить итерации по кортежам. В TypeScript 3.4.x нет аналога for. В идеале нужен счетчик:

Освой карринг и шаблоны TypeScript быстро и безболезненно

Проверяем:

Освой карринг и шаблоны TypeScript быстро и безболезненно

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

Освой карринг и шаблоны TypeScript быстро и безболезненно

Prepend

Prepend добавляет тип E поверх кортежа T :

Освой карринг и шаблоны TypeScript быстро и безболезненно

Проверка:

Освой карринг и шаблоны TypeScript быстро и безболезненно

В примере с Length мы увеличивали счетчик вручную. Prepend – идеален как основа для счетчика. Вот так он работает:

Освой карринг и шаблоны TypeScript быстро и безболезненно

Drop принимает кортеж T и удаляет первые N записей. Для этого используем те же приемы, что и в Last :

Освой карринг и шаблоны TypeScript быстро и безболезненно

Проверка:

Освой карринг и шаблоны TypeScript быстро и безболезненно

Drop будет повторяться до тех пор, пока Length<I> не совпадет со значением N , которое мы передали. Другими словами, тип индекса 0 выбирается условным методом доступа до тех пор, пока это условие не будет выполнено. + мы использовали Prepend , чтобы увеличить счетчик, как в цикле. Так что Length<I> используется в качестве счетчика рекурсии, и это способ свободной итерации в TS.

Ты проделал нелегкий путь, добравшись сюда, и это здорово!

Предположим, теперь мы можем отследить, как 2 параметра используются каррированной функцией:

Освой карринг и шаблоны TypeScript быстро и безболезненно

С Drop узнаем количество употребленных и не использованных параметров:

Освой карринг и шаблоны TypeScript быстро и безболезненно

Обновим предыдущую версию со сломанным Tail :

Освой карринг и шаблоны TypeScript быстро и безболезненно

Что же мы сделали?

Во-первых, Drop<Length<T>, P> означает удаление употребленных параметров. Затем, если длина Drop<Length<T>, P> не равна 0 , тип карринга должен продолжать рекурсию с отброшенными параметрами, пока. Наконец, когда все параметры употреблены, Length отброшенных параметров равна 0 , а возвращаемый тип – R .

Есть еще одна ошибка выше: TS жалуется, что Drop не относится к типу [] . Иногда TS жалуется на неожиданный тип, несмотря на то, что он подходит! Это повод добавить еще один инструмент в коллекцию:

Cast требует, чтобы TS перепроверил тип X с типом Y , и тип Y будет применен только в случае неудачи. Так можно предотвратить жалобы TS:

Освой карринг и шаблоны TypeScript быстро и безболезненно

Освой карринг и шаблоны TypeScript быстро и безболезненно

И вот предыдущий карринг без каких-либо жалоб:

Освой карринг и шаблоны TypeScript быстро и безболезненно

Освой карринг и шаблоны TypeScript быстро и безболезненно

Освой карринг и шаблоны TypeScript быстро и безболезненно

Освой карринг и шаблоны TypeScript быстро и безболезненно

Мы все еще не можем взять оставшиеся параметры. И вот почему:

Освой карринг и шаблоны TypeScript быстро и безболезненно

Поскольку количество оставшихся параметров может быть неограниченными, TS полагает, что длина нашего кортежа – это число number . Поэтому нельзя использовать Length при работе с оставшимися параметрами, что не так плохо:

Освой карринг и шаблоны TypeScript быстро и безболезненно

При использовании параметров, Drop<Length<T>,P> может проверять только […any[]] . Благодаря этому мы использовали [any,…any[]] в качестве условия для завершения рекурсии.

Освой карринг и шаблоны TypeScript быстро и безболезненно

Освой карринг и шаблоны TypeScript быстро и безболезненно

Освой карринг и шаблоны TypeScript быстро и безболезненно

Все работает! Теперь у тебя есть универсальный, вариативный тип карринига. Как насчет дальнейших улучшений?

Плейсхолдеры

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

Освой карринг и шаблоны TypeScript быстро и безболезненно

Плейсхолдер или «пробел» – это объект, который абстрагирует факт отсутствия аргумента для передачи в определенный момент. Начнем с определения плейсхолдера. Обратимся напрямую к Ramda:

Освой карринг и шаблоны TypeScript быстро и безболезненно

Мы уже знаем, как выполнять первые итерации типов, увеличивая длину кортежа. Но использование Length и Prepend для нашего типа счетчика не вносит ясности. С этого момента мы будем обращаться к счетчику как к итератору. Вот новые псевдонимы для этого:

Pos (Позиция)

Используйте его для запроса позиции итератора:

Освой карринг и шаблоны TypeScript быстро и безболезненно

Next (+1)

Поднимает позицию итератора:

Освой карринг и шаблоны TypeScript быстро и безболезненно

Prev (-1)

Снижает позицию итератора:

Освой карринг и шаблоны TypeScript быстро и безболезненно

Проверка:

Освой карринг и шаблоны TypeScript быстро и безболезненно

Итератор

Он создает итератор (наш тип счетчика) в позиции, определяемой Index , и может начинать с позиции другого итератора, используя From :

Освой карринг и шаблоны TypeScript быстро и безболезненно

Проверка:

Освой карринг и шаблоны TypeScript быстро и безболезненно

Основные инструменты №2

Отлично, что делаем дальше? Нужно проанализировать передачу плейсхолдера в качестве аргумента. Так мы поймем, был параметр «пропущен» или «отложен». Вот инструменты для этой цели:

Reverse

Reverse даст необходимую свободу. Он принимает кортеж T и превращает его в кортеж R благодаря новым типам итераций:

Освой карринг и шаблоны TypeScript быстро и безболезненно

Проверка:

Освой карринг и шаблоны TypeScript быстро и безболезненно

Concat

И родился из Reverse Concat . Он объединяет кортежи T1 и T2 . Мы делали это в test59 :

Освой карринг и шаблоны TypeScript быстро и безболезненно

Освой карринг и шаблоны TypeScript быстро и безболезненно

Append

С помощью Concat Append может добавить тип E в конец кортежа T :

Освой карринг и шаблоны TypeScript быстро и безболезненно

Освой карринг и шаблоны TypeScript быстро и безболезненно

Вот и готовы инструменты для типа каррирования. Gaps – это новая замена Partial , а GapsOf заменит Drop :

Освой карринг и шаблоны TypeScript быстро и безболезненно

Освой карринг и шаблоны TypeScript быстро и безболезненно

Для проверки принудительно установим значения, которые будут взяты с каррированной функцией:

Освой карринг и шаблоны TypeScript быстро и безболезненно

Освой карринг и шаблоны TypeScript быстро и безболезненно

Упс! Маленькая проблема. Дело в том, что мы «опередили» Ramda! Наш тип понимает сложные использования плейсхолдеров. Другими словами, плейсхолдеры Ramda просто не работают с оставшимися параметрами:

Освой карринг и шаблоны TypeScript быстро и безболезненно

Освой карринг и шаблоны TypeScript быстро и безболезненно

Несмотря на то, что все все выглядит правильно, мы получим падение. Реализация карринга Ramda не справляется с комбинацией плейсхолдеров и оставшихся параметров.

Карринг

Осталось решить последнюю проблему с подсказками параметров. Полезно знать названия параметров, с которыми работаешь. Версия выше не допускает такого рода подсказок. Вот исправление:

Освой карринг и шаблоны TypeScript быстро и безболезненно

Мы получили подсказки для Visual Studio Code. Как? Да просто заменили типы параметров P и R , которые использовались для обозначения типов параметра и возврата. Вместо этого мы использовали тип функции F , из которого извлекли эквивалент Parameters<F> и R с ReturnType<F> . Так, TypeScript способен сохранять имя параметров даже после каррирования:

Вы пропустили

AEGIS Algorithms Android Angular Apache Airflow Apache Druid Apache Flink Apache Spark API API Canvas AppSec Architecture Artificial Intelligence Astro Authentication Authorization AutoGPT AWS AWS Aurora AWS Boto3 AWS EC2 AWS Lambda Azure Babylon.js Backend bash Beautiful Soup Bento UI Big Data Binary Tree Browser API Bun Career Cassandra Charts ChatGPT Chrome Extension Clean Code CLI ClickHouse Coding Codux Combine Compose Computer Context Fusion Copilot Cosmo Route CProgramming cron Cryptography CSS CTF Cypress DALL-E Data Analysis Data science Database dbt dbt Cloud deno Design Design Patterns Detekt Development Distributed Systems Django Docker Docker Hub Drizzle DRY DuckDB Express FastAPI Flask Flutter For Beginners Front End Development Game Development GCN GCP Geospatial Git GitHub Actions GitHub Pages Gitlab GMS GoFr Golang Google Google Sheets Google Wire GPT-3 GPT3 Gradio Gradle Grafana Graphic Design GraphQL gRPC Guidance HMS Hotwire HTML Huawei HuggingFace IndexedDB InfoSec Interview iOS Jackknife Java JavaScript Jetpack Compose JSON Kafka Kotlin Kubernetes LangChain Laravel Linux LlaMA LLM localStorage Logging Machine Learning Magento Math Mermaid Micro Frontends Mobile Mobile App Development mondayDB MongoDB Mongoose MySQL Naming NestJS NET NetMock Networks NextJS NLP Node.js Nodejs NoSQL NPM OOP OpenAI OTP Pandas PDF PHP Playwright Plotly Polars PostgreSQL Prefect Productivity Programming Prometheus Puppeteer Pushover Python Pytorch Quarkus Rabbitmq RAG Ramda Raspberry Pi React React Native Reactor Redis REST API Revolut Riverpod RProgramming Ruby Ruby on Rails Rust Scalene SCDB ScyllaDB Selenium Servers Sklearn SLO SnowFlake Snowkase Software Architecture Software Development Solara Solid Spring Boot SQL SQLite Streamlit SudoLang Supabase Swift SwiftUI Tailwind CSS Taipy Terraform Testing Transformers TURN TypeScript Ubuntu UI Design Unix UX UX Design Vim Vite VSCode Vue Web Architecture Web Components Web Development Web Frameworks Web Scraping Web-разработка Webassembly Websocket Whisper Widgets WordPress YAML YouTube Zed Наука о данных Разное Тренды

Как исследовать и визуализировать данные МО для обнаружения объектов на изображениях