Проектирование плавного 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 — локальная установка не требуется.
Все уроки этого курса
- Что такое DSL на уровне типов
- Проектирование плавного DSL запросов
- Проверка входных данных во время компиляции
- Сообщения об ошибках в DSL на уровне типов