Kiedyś formułowałem tę tezę celowo prowokacyjnie: TypeScript to najlepszy język dla Codex. Dziś powiedziałbym to precyzyjniej. W produkcyjnej pracy frontendowej i full-stack, którą wykonuję, TypeScript jest środowiskiem, w którym Codex najłatwiej kontrolować, weryfikować i używać z zaufaniem.
Najważniejszym słowem nie jest „TypeScript”, tylko kontrola. Agenci AI świetnie tworzą kod, który wygląda wiarygodnie. Produkcja wymaga czegoś więcej: kod musi pasować do istniejących kontraktów, zachować zachowanie, przeżyć refaktoryzację i szybko zgłosić błąd, gdy założenie jest niepoprawne. TypeScript sprawia, że znacznie więcej tych ograniczeń jest czytelnych dla maszyny niż w czystym JavaScript.
Czego Codex naprawdę potrzebuje od codebase
Dobry prompt pomaga, ale jest tylko jednym źródłem kontekstu. Sama codebase może ułatwiać rozumowanie albo zmuszać model do zgadywania. W praktyce najlepsze środowisko to takie, w którym ważne założenia są widoczne i automatycznie sprawdzane.
- Sygnatury funkcji określają poprawne wejścia i wyjścia.
- Interfejsy i typy ujawniają kształt obiektów domenowych.
- Uniony i enumy ograniczają zestaw poprawnych stanów.
- Kompilator zamienia wiele złych założeń w konkretne błędy natychmiast po zmianie.
Agent AI nie musi perfekcyjnie rozumieć całej aplikacji, jeśli aplikacja sama reaguje, gdy złamie kontrakt. Im krótsza pętla feedbacku, tym mniej miejsca na pewne siebie zgadywanie.
Typy są wykonywalnym kontekstem, nie tylko dokumentacją
Dobry TypeScript dokumentuje intencję dla człowieka, ale w automatycznym workflow daje coś więcej: tę dokumentację można sprawdzić. Na przykład:
type PaymentResult =
| { ok: true; receiptId: string }
| { ok: false; code: "declined" | "timeout" };
function getReceiptId(result: PaymentResult) {
if (result.ok) return result.receiptId;
return result.receiptId;
// TypeScript: Property 'receiptId' does not exist
// on the failure branch.
}W JavaScript ten sam błąd może wyglądać rozsądnie, dopóki dana gałąź nie wykona się w runtime. TypeScript może odrzucić błędne założenie jeszcze przed mergem. Dla Codex błąd kompilatora nie jest tylko czerwoną linią w edytorze, ale precyzyjnym sygnałem, co model źle zrozumiał.
Dlatego wolę jawne typy domenowe od szerokich konstrukcji typu Record<string, any>. Im dokładniej typy opisują rzeczywistość, tym lepszym kontekstem codebase staje się dla ludzi i AI.
Kompilator zamienia błędy w pętlę feedbacku
Nie ufam procesowi „poproś Codex o kod i zaakceptuj pierwszą odpowiedź”. Workflow jest iteracyjny:
task → inspect → edit → typecheck → lint → test → review diffTo nudna sekwencja — i właśnie dlatego działa. Po każdej sensownej zmianie środowisko odpowiada. Type errors wykrywają złamane kontrakty. Lint łapie inną klasę problemów. Testy weryfikują zachowanie, którego typy nie potrafią wyrazić. Finalny diff nadal przechodzi ludzki review.
Sprytny prompt może poprawić pierwszą próbę. Mocna pętla weryfikacji poprawia każdą próbę. W produkcji to znacznie ważniejsze.
Duże refaktoryzacje pokazują największą wartość TypeScript
Małe lokalne zmiany są łatwe dla prawie każdego asystenta. Trudność pojawia się, gdy modyfikacja przechodzi przez dziesiątki plików: zmiana nazwy pola domenowego, formatu odpowiedzi API, zaostrzenie propsów, podział unionu czy wymiana starej abstrakcji.
W TypeScript breaking change tworzy mapę naruszonych założeń. Kompilator pokazuje miejsca, w których stary kontrakt nadal żyje. Codex może przejść przez tę listę i po każdej rundzie ponownie uruchomić weryfikację. To nie gwarantuje poprawności refaktoru, ale pokazuje blast radius.
W luźno typowanym projekcie pominięta zależność często ujawnia się dopiero jako bug na ekranie, którego nikt nie otworzył podczas developmentu.
Przed czym TypeScript nie chroni
TypeScript jest guardrailem, nie dowodem poprawności. Nie zapobiega ważnym kategoriom błędów:
- Zła logika biznesowa: perfekcyjnie typowany kod nadal może liczyć coś błędnie.
- Dane runtime: API, baza, formularz lub serwis zewnętrzny mogą zwrócić dane niezgodne z założeniami compile-time.
- Brak kontekstu produktu: kompilator nie wie, po co istnieje dany flow i który edge case jest istotny dla użytkownika.
- Błędy bezpieczeństwa i architektury: poprawne typy nie gwarantują właściwej autoryzacji, cache, granic bazy ani infrastruktury.
Runtime validation, testy, logging, observability i review pozostają konieczne. Czysty typecheck nie oznacza, że feature jest poprawna.
Workflow, którego używam przy zmianach wspieranych przez AI
- Ustalam wąski cel. Co ma się zmienić, czego nie ruszać i jakie są acceptance criteria.
- Każę Codex najpierw zbadać projekt. Istniejące typy, call sites, testy i sąsiednie moduły często są lepszym kontekstem niż długi prompt.
- Wybieram najmniejszą spójną zmianę. Skupiony diff łatwiej zweryfikować niż „pomocną” przebudowę niepowiązanego kodu.
- Od razu uruchamiam typecheck i lint. Błędy strukturalne chcę zobaczyć, gdy zmiana jest jeszcze mała.
- Uruchamiam właściwe testy i dodaję testy przy zmianie zachowania. Typy sprawdzają kontrakty, testy — zachowanie.
- Finalny diff sprawdzam ręcznie. Nazwy, architektura, duplikacja, edge cases i to, czy implementacja rozwiązuje pierwotny problem.
Kiedy JavaScript nadal wystarcza
To nie jest argument, że JavaScript jest zły. Dla małego skryptu, jednorazowego prototypu, prostej automatyzacji czy kodu z małą ilością współdzielonego stanu dodatkowa struktura TypeScript może niewiele dać. Projekt JavaScript z dobrymi testami i jasnymi konwencjami również może być świetnym środowiskiem dla AI.
Moja teza jest węższa: im większa, dłużej żyjąca i bardziej zespołowa codebase, tym większa wartość ograniczeń sprawdzalnych przez maszynę. I dokładnie wtedy błędy AI stają się droższe.
Prawdziwy powód, dla którego wybieram TypeScript z Codex
TypeScript nie sprawia, że Codex staje się mądrzejszy. Sprawia, że środowisko mniej toleruje złe założenia.
W produkcji nie chcę tylko kreatywnego agenta; chcę agenta działającego w systemie, który stale informuje go, kiedy się myli. Typy, błędy kompilatora, lint, testy i ludzki review wspólnie tworzą taki system.
Dlatego moje „TypeScript świetnie działa z Codex” nie jest fanatyzmem językowym. Chodzi o szybki, maszynowo weryfikowalny feedback: mniej niejednoznaczności, mniejszy blast radius, bezpieczniejsze refaktoryzacje i — jeśli naprawdę przestrzega się weryfikacji — znacznie bardziej niezawodną szybkość inżynierską.