Treci la conținutul principal

TypeScript cu React

Acest capitol arată cum să pornești un proiect React cu TypeScript și cum să tipizezi lucrurile pe care le întâlnești zilnic: props, evenimente, stare, ref-uri, copii (children), preluarea datelor și contextul.


De ce să folosești TypeScript cu React?

TypeScript îmbunătățește React cu:

  • Siguranța tipurilor pentru props, stare și context
  • Autocompletare și refactorizare mai bune în IDE
  • Erori prinse devreme, chiar în timp ce scrii codul

Notă: Acest capitol presupune că știi bazele React. Dacă ești începător în React, parcurge mai întâi un tutorial de React.


Primii pași

Creează o nouă aplicație React + TypeScript cu Vite rulând aceste comenzi:

  • npm create vite@latest my-app -- --template react-ts
  • cd my-app
  • npm install
  • npm run dev

Fișierul tău tsconfig.json ar trebui să aibă aceste compilerOptions recomandate:

  • "target": "ES2020"
  • "lib": ["ES2020", "DOM", "DOM.Iterable"]
  • "module": "ESNext"
  • "moduleResolution": "Node"
  • "jsx": "react-jsx"
  • "strict": true
  • "skipLibCheck": true
  • "noEmit": true
  • "resolveJsonModule": true
  • "allowSyntheticDefaultImports": true
  • "esModuleInterop": true
  • "forceConsistentCasingInFileNames": true

împreună cu "include": ["src"] alături.

Notă: Păstrează strict activat pentru cea mai bună siguranță a tipurilor. Aceste opțiuni funcționează bine cu Vite și Create React App.


Tipizarea componentelor

Descrie props-urile cu un tip, apoi folosește acel tip într-o componentă funcție:

Props tipizate într-o componentă funcție

GreetingProps are un name obligatoriu și un age opțional; componenta afișează linia cu vârsta doar când aceasta este dată.

script.ts
// Greeting.tsx
 type GreetingProps = {
   name: string;
   age?: number;
 };

 export function Greeting({ name, age }: GreetingProps) {
   return (
     <div>
       <h2>Hello, {name}!</h2>
       {age !== undefined && <p>You are {age} years old</p>}
     </div>
   );
 }

Tipare frecvente

Acestea sunt tiparele de tipizare pe care le vei folosi cel mai des într-o aplicație React.

Evenimente sigure ca tip. Dă handler-elor de evenimente pentru câmpuri de intrare și butoane tipul de eveniment React potrivit.

Tipizarea stării cu useState. Folosește argumente de tip explicite pentru numere, uniuni și valori care pot fi null.

useRef cu elemente DOM. Tipizează un ref cu elementul DOM pe care îl va conține, ca să îi poți citi proprietățile în siguranță.

Tipizarea copiilor (children). Acceptă copiii cu tipul React.ReactNode.

Funcții ajutătoare de fetch cu generice. Folosește o funcție generică pentru a tipiza răspunsurile API.

Context minimal și hook personalizat. Oferă un context mic și tipizat, plus un hook ajutător care garantează că este disponibil.

Tipurile TypeScript pentru Vite: Adaugă tipurile ambientale ale lui Vite, ca să nu lipsească nimic. Creează src/vite-env.d.ts care să conțină /// <reference types="vite/client" /> sau adaugă "types": ["vite/client"] în compilerOptions din tsconfig.json.

Despre React.FC: Preferă componente funcție tipizate simplu. React.FC este opțional; adaugă children implicit, dar nu este obligatoriu.

baseUrl și paths opționale: Acestea pot scurta importurile dacă bundler-ul tău le suportă. Setează "baseUrl": "." și "paths": { "@/*": ["src/*"] } în compilerOptions. Configurează-le doar dacă uneltele tale (de exemplu Vite sau tsconfig-paths) sunt pregătite pentru alias-uri de căi.

Handler-e de evenimente sigure ca tip

React.ChangeEvent<HTMLInputElement> și React.MouseEvent<HTMLButtonElement> îți dau acces tipizat la eveniment și la ținta lui.

script.ts
// Input change
 function NameInput() {
   function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
     console.log(e.target.value);
   }
   return <input onChange={handleChange} />;
 }

 // Button click
 function SaveButton() {
   function handleClick(e: React.MouseEvent<HTMLButtonElement>) {
     e.preventDefault();
   }
   return <button onClick={handleClick}>Save</button>;
 }

Tipizarea stării cu useState

Un număr, o uniune de literale de tip șir de caractere și un obiect care poate fi null, fiecare cu un argument de tip explicit.

script.ts
const [count, setCount] = React.useState<number>(0);
 const [status, setStatus] = React.useState<'idle' | 'loading' | 'error'>('idle');

 type User = { id: string; name: string };
 const [user, setUser] = React.useState<User | null>(null);

useRef cu un element DOM

Ref-ul este tipizat ca HTMLInputElement, așa că inputRef.current?.select() este verificat de compilator.

script.ts
function FocusInput() {
   const inputRef = React.useRef<HTMLInputElement>(null);
   return <input ref={inputRef} onFocus={() => inputRef.current?.select()} />;
 }

Tipizarea copiilor (children)

children este opțional și tipizat ca React.ReactNode, așa că este acceptat orice poate afișa React.

script.ts
 type CardProps = { title: string; children?: React.ReactNode };
 function Card({ title, children }: CardProps) {
   return (
     <div>
       <h2>{title}</h2>
       {children}
     </div>
   );
 }

Funcție ajutătoare generică de fetch

fetchJson<T> returnează un Promise<T>, așa că apelantul decide forma răspunsului.

script.ts
async function fetchJson<T>(url: string): Promise<T> {
   const res = await fetch(url);
   if (!res.ok) throw new Error('Network error');
   return res.json() as Promise<T>;
 }

 // Usage inside an async function/component effect
 async function loadPosts() {
   type Post = { id: number; title: string };
   const posts = await fetchJson<Post[]>("/api/posts");
   console.log(posts);
 }

Context tipizat și hook personalizat

Valoarea contextului este fie obiectul temei, fie null; useTheme aruncă o eroare dacă este folosit în afara provider-ului.

script.ts
 type Theme = 'light' | 'dark';
 const ThemeContext = React.createContext<{ theme: Theme; toggle(): void } | null>(null);

 function ThemeProvider({ children }: { children: React.ReactNode }) {
   const [theme, setTheme] = React.useState<Theme>('light');
   const value = { theme, toggle: () => setTheme(t => (t === 'light' ? 'dark' : 'light')) };
   return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;
 }

 function useTheme() {
   const ctx = React.useContext(ThemeContext);
   if (!ctx) throw new Error('useTheme must be used within ThemeProvider');
   return ctx;
 }