Projektowanie fluent query DSL
Buduj łańcuchowe API zapytań, które samo sprawdza poprawność
Projektowanie fluent query DSL to bezpłatna lekcja TypeScript Academy na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej TypeScript Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs TypeScript Academy zawiera 4 lekcji w sumie.
Projektowanie fluent DSL zapytań
Projektujemy łańcuchowy DSL zapytań, w którym każdy krok uszczegóławia dozwolone kolejne kroki za pomocą typów zwracanych. Rezultat przypomina SQL, a niepoprawne kolejności są odrzucane podczas kompilacji.
Docelowa gramatyka
Celem jest: from, następnie opcjonalne, możliwe do powtórzenia where, a na końcu select. Nie można użyć select przed from ani użyć from dwukrotnie.
Interfejsy stanów
Każdy etap należy zamodelować jako interfejs zwracający kolejny etap.
interface Builder {
from(table: string): FromStage;
}
interface FromStage {
where(cond: string): FromStage; // repeatable
select(...cols: string[]): Result;
}
interface Result { sql: string; }Wymuszanie kolejności
Ponieważ select istnieje wyłącznie w FromStage, wywołanie go na początkowym Builder powoduje błąd kompilacji. Kolejność jest wymuszana wyłącznie przez metody udostępniane przez poszczególne etapy.
declare const db: Builder;
db.from("users").select("id"); // ok
db.select("id"); // Error: select missing on BuilderŚledzenie wybranych kolumn
Należy dodać fantomowy generyk, aby zapamiętać wybrane kolumny i zapewnić precyzyjny typ wyniku.
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[]; }Uszczegóławianie przy każdym wywołaniu
Każde where może również dodawać ograniczenia do typu. Tutaj zachowujemy prostotę, ale ten wzorzec można uogólnić do śledzenia powiązanych parametrów.
const q = db.from("users").where("age > 18").where("active = true");
// still FromStage; select remains availableZapobieganie powtórnemu użyciu from
Ponieważ FromStage nie udostępnia from, nie można wywołać go dwukrotnie. Gramatyka zabrania tego strukturalnie, więc nie jest potrzebne zabezpieczenie w czasie działania.
db.from("a").from("b"); // Error: from does not exist on FromStageKrok końcowy
select zwraca Result, który nie udostępnia ani where, ani from, kończąc łańcuch. Pozostają wyłącznie operacje odczytu wyniku.
const r = db.from("users").select("id", "name");
r.columns; // ("id" | "name")[]
// r.where(...) -> Error: where not on ResultTypowane ograniczenia kolumn
Należy ograniczyć kolumny do znanego schematu tabeli za pomocą kolejnego generyka, aby odrzucać nieznane kolumny i połączyć ten DSL z wcześniejszymi koncepcjami ORM.
interface Table<Cols extends string> {
select<C extends Cols>(...cols: C[]): Result<C>;
}
// db.from gives Table<"id" | "name" | "age">Kroki opcjonalne a wymagane
Krok można uczynić wymaganym, udostępniając kolejną metodę dopiero po jego wykonaniu. Na przykład można wymusić co najmniej jedno where, zwracając etap, na którym select pojawia się dopiero po wywołaniu where. Te same typy łańcucha mogą również wywnioskować typ wiersza wyniku wykonania zapytania, łącząc gramatykę sprawdzaną podczas kompilacji z danymi w czasie działania.
Dlaczego to ma znaczenie
Zaprojektowany w ten sposób fluent DSL sam się dokumentuje i nie można go niewłaściwie użyć: autouzupełnianie pokazuje wyłącznie poprawne kolejne kroki, a nielegalne sekwencje nigdy się nie kompilują. To fundament ergonomicznych bibliotek builderów.
Szybki test
Proszę sprawdzić, czy rozumieją Państwo projektowanie fluent DSL.
Podsumowanie
Fluent DSL zapytań został zaprojektowany jako maszyna stanów na poziomie typów: każdy interfejs etapu zwraca kolejny i udostępnia wyłącznie poprawne metody. Fantomowe generyki śledzą wybrane kolumny, etapy końcowe zamykają łańcuch, a ograniczenia kolumn odrzucają nieznane nazwy — wszystko jest wymuszane przez typy zwracane.
Często zadawane pytania
Czy lekcja „Projektowanie fluent query DSL” jest bezpłatna?
Tak — pełny tekst „Projektowanie fluent query DSL” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu TypeScript Academy, przejdź na CoddyKit PRO. Kurs TypeScript Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Projektowanie fluent query DSL”?
Buduj łańcuchowe API zapytań, które samo sprawdza poprawność Ćwiczysz TypeScript Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć TypeScript Academy?
Nie wymagamy żadnego doświadczenia. TypeScript Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.
Ile czasu zajmuje lekcja „Projektowanie fluent query DSL”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji TypeScript Academy?
Tak. Każda lekcja TypeScript Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Czym jest DSL na poziomie typów
- Projektowanie fluent query DSL
- Walidacja danych wejściowych w czasie kompilacji
- Komunikaty błędów w DSL na poziomie typów