0Pricing
TypeScript Academy · Урок

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

Создавайте объединяемый в цепочки API запросов с самопроверкой

«Проектирование плавного DSL запросов» — бесплатный урок TypeScript Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения TypeScript Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс TypeScript Academy содержит 4 уроков всего.

Проектирование цепочного DSL запросов

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

Целевая грамматика

Нам нужна такая последовательность: сначала from, затем необязательный и повторяемый where, а после него завершающий select. Нельзя вызвать select до from и нельзя вызвать from дважды.

Интерфейсы состояний

Представьте каждый этап как интерфейс, возвращающий следующий этап.

interface Builder {
  from(table: string): FromStage;
}
interface FromStage {
  where(cond: string): FromStage; // repeatable
  select(...cols: string[]): Result;
}
interface Result { sql: string; }

Обеспечение порядка

Поскольку select существует только в FromStage, его вызов для исходного Builder приводит к ошибке компиляции. Порядок обеспечивается исключительно тем, какие методы предоставляет каждый этап.

declare const db: Builder;
db.from("users").select("id"); // ok
db.select("id");               // Error: select missing on Builder

Отслеживание выбранных столбцов

Добавьте фантомное обобщение, чтобы запомнить выбранные столбцы и получить точный тип результата.

interface FromStage<T extends string = never> {
  where(c: string): FromStage<T>;
  select<C extends string>(...cols: C[]): Result<C>;
}
interface Result<C extends string> { columns: C[]; }

Уточнение при каждом вызове

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

const q = db.from("users").where("age > 18").where("active = true");
// still FromStage; select remains available

Запрет повторного from

Поскольку FromStage не предоставляет from, вызвать его дважды нельзя. Грамматика запрещает это структурно, поэтому проверка во время выполнения не требуется.

db.from("a").from("b"); // Error: from does not exist on FromStage

Конечный этап

select возвращает Result, который не предоставляет ни where, ни from, завершая цепочку. Остаются только операции чтения результата.

const r = db.from("users").select("id", "name");
r.columns; // ("id" | "name")[]
// r.where(...) -> Error: where not on Result

Типизированные ограничения столбцов

Ограничьте столбцы известной схемой таблицы с помощью ещё одного обобщения, чтобы неизвестные столбцы отклонялись; так этот DSL объединяется с рассмотренными ранее идеями ORM.

interface Table<Cols extends string> {
  select<C extends Cols>(...cols: C[]): Result<C>;
}
// db.from gives Table<"id" | "name" | "age">

Необязательные и обязательные этапы

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

Почему это важно

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

Быстрая проверка

Проверьте своё понимание проектирования цепочного DSL.

Итоги

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

Часто задаваемые вопросы

Урок «Проектирование плавного DSL запросов» бесплатный?

Да — полный текст урока «Проектирование плавного DSL запросов» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс TypeScript Academy, подпишись на CoddyKit PRO. Курс TypeScript Academy содержит 4 уроков всего.

Чему я научусь в уроке «Проектирование плавного DSL запросов»?

Создавайте объединяемый в цепочки API запросов с самопроверкой Ты практикуешь TypeScript Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать TypeScript Academy?

Предыдущий опыт не требуется. TypeScript Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.

Сколько времени занимает урок «Проектирование плавного DSL запросов»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке TypeScript Academy?

Да. Каждый урок TypeScript Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Что такое DSL на уровне типов
  2. Проектирование плавного DSL запросов
  3. Проверка входных данных во время компиляции
  4. Сообщения об ошибках в DSL на уровне типов
← Назад к TypeScript Academy