interface UserProps {
id: number;
name: string;
email?: string; // opțional
}
const UserProfile = ({ id, name, email }: UserProps) => {
const [data, setData] = useState<UserProps | null>(null);
const inputRef = useRef<HTMLInputElement>(null);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
console.log(e.target.value);
};
return (
<div>
<h1>{name}</h1>
<input ref={inputRef} onChange={handleChange} />
</div>
);
};Am trecut prima oară un proiect de React pe TypeScript prin 2018. Era un dashboard intern pentru vreo 50 de utilizatori și m-am simțit ca și cum aș fi învățat să merg din nou pe bicicletă, dar cu roțile pătrate. Dacă vii din JS chior, TypeScript pare un polițist care te oprește la fiecare intersecție să te întrebe de buletin. Dar adevărul e că, după ce am trecut de faza de acomodare, am redus bug-urile de tip 'undefined is not a function' cu vreo 80% în producție.
Nu te lăsa păcălit de tentația de a pune any peste tot. E ca și cum ai purta centura de siguranță, dar nu ai închide-o. Hai să vedem unde se împiedică majoritatea la început și cum să o faci corect.
1. Definirea tipurilor pentru Props
Cea mai comună greșeală e să nu definești un contract clar pentru componente. Am văzut zeci de proiecte unde se folosea React.FC, dar faza e că FC (FunctionComponent) aducea niște bagaj extra, cum ar fi children implicit (în versiunile mai vechi), ceea ce nu era mereu ce voiai.
Eu prefer să folosesc interfețe simple. E mult mai curat și te forțează să fii explicit. Dacă ai un buton, definește exact ce primește. Trade-off-ul e că scrii cu 5 linii de cod mai mult, dar când schimbi un prop peste 6 luni în alt fișier, TS o să-ți urle exact unde ai stricat logica.
2. useState și valorile inițiale null
Aici e buba mare. Faci un fetch, aștepți un obiect, dar inițial ai null. Dacă scrii useState(null), TypeScript va infera că starea ta e null pentru totdeauna.
Trebuie să folosești generics: useState<User | null>(null). Altfel, când încerci să accesezi user.name, compilerul o să-ți dea peste degete. Am pățit asta la un proiect cu un flux de checkout complex și m-am prins târziu că jumătate din erorile de runtime erau din cauza presupunerii că datele sunt mereu acolo.
3. Typing la Evenimente (Input-uri și Formulare)
Când scrii un handler de onChange, e frustrant să nu știi ce tip are e. Mulți pun any pentru că e rapid. Dar dacă vrei să fii profi, folosește React.ChangeEvent<HTMLInputElement>.
De ce contează? Pentru că primești autocomplete la e.target.value și ești sigur că acel handler e atașat unde trebuie. Dacă muți handler-ul de pe un input pe un select, TS o să-ți spună dacă ceva nu mai pușcă.
4. useRef și accesul la DOM
useRef e un alt loc unde lumea se încurcă. Dacă vrei să muți focusul pe un input, trebuie să fii specific: useRef<HTMLInputElement>(null).
Și nu uita de verificarea de null. De fiecare dată când folosești inputRef.current, trebuie să ai un if (inputRef.current) înainte. Pare redundant, dar te scapă de crash-uri idioate când componenta nu e încă montată.
5. Răspunsurile din API
Aici am văzut cele mai multe probleme de scalabilitate. Lucrezi cu axios sau fetch, primești un JSON și îl arunci într-o stare. Dacă nu tipizezi acel răspuns chiar la intrare, tot restul aplicației tale va fi o ghicitoare.
Eu îmi fac mereu un folder de types unde definesc modelele de date de pe backend. Dacă backend-ul schimbă un field din user_id în id, schimb într-un singur loc și văd imediat cele 20 de erori în componentele care foloseau acel field.
TypeScript nu e despre a scrie cod mai repede, ci despre a dormi mai liniștit noaptea. La început o să pierzi cam 20% mai mult timp cu setup-ul, dar o să economisești ore întregi la refactoring.
Voi cum gestionați frustrarea aia de la început când compilerul vă dă erori pe care nu le înțelegeți?