import React, { useState, useRef, ChangeEvent } from 'react';
type User = {
id: number;
name: string;
};
type UserCardProps = {
title: string;
children: React.ReactNode;
};
export const UserProfile = ({ title, children }: UserCardProps) => {
const [users, setUsers] = useState<User[]>([]);
const [name, setName] = useState<string>('');
const inputRef = useRef<HTMLInputElement>(null);
const handleNameChange = (e: ChangeEvent<HTMLInputElement>) => {
setName(e.target.value);
};
const handleFocus = () => {
inputRef.current?.focus();
};
return (
<div>
<h2>{title}</h2>
<input ref={inputRef} value={name} onChange={handleNameChange} />
<button onClick={handleFocus}>Focus Input</button>
<div>{children}</div>
</div>
);
};Când am migrat primul proiect serios de la JS la TypeScript strict — era o aplicație cu vreo 12k utilizatori activi — am dat build și m-am trezit cu 140 de erori în terminal. Jumătate dintre ele nu erau bug-uri reale din cod, ci doar neînțelegeri între cum funcționează React și cum validează TypeScript tipurile. Dacă ești la primul tău proiect cu strict: true în tsconfig.json, o să te lovești fix de aceleași ziduri.
Nu e nevoie să te sperii sau să pui // @ts-ignore peste tot. Haide să trecem prin cele 5 erori clasice și să vedem cum le rezolvi corect.
1. useState([]) devine never[]
Scrii const [users, setUsers] = useState([]) și totul pare ok până când încerci să faci setUsers([{ id: 1, name: 'Alex' }]). TypeScript îți aruncă direct o eroare prin care îți spune că un obiect nu poate fi asignat tipului never.
Când îi dai o listă goală fără un tip explicit, TypeScript presupune că acea listă va rămâne mereu goală (never[]). Soluția este să folosești un tip generic explicit la inițializare: useState<User[]>([]). Dacă starea poate fi inițial null, folosești tipul uniune: useState<User | null>(null).
2. e.target.value la evenimente de Input
La un simplu onChange, scrii (e) => setName(e.target.value) și TypeScript se plânge că e are tipul any implicit (dacă ai noImplicitAny: true) sau că target nu are proprietatea value.
Evenimentele din React nu sunt evenimentele nativ de DOM, ci wrapper-e numite SyntheticEvent. Pentru un input de text, tipul corect pentru parametru este React.ChangeEvent<HTMLInputElement>. Dacă e un buton de submit într-un formular, vei folosi React.FormEvent<HTMLFormElement>. Odată ce pui tipul ăsta, ai și autocompletion pe e.target.value.
3. useRef(null) și eroarea de Read-Only
Dacă vrei să ții o referință către un element din DOM, probabil scrii const inputRef = useRef<HTMLInputElement>(null). Când încerci mai târziu să faci inputRef.current = elem, TypeScript îți spune că current este read-only.
Asta se întâmplă din cauza suprapunerii de semnături din definițiile React. Dacă îi pasezi null ca valoare inițială și folosești tipul simplu, TypeScript crede că vrei un Ref imutabil (creat de React.createRef). Rezolvarea e simplă: dacă chiar vrei să schimbi valoarea manual, adaugi | null în genericul ref-ului sau inițializezi fără argumente. Totuși, pentru elemente DOM atașate prin prop-ul ref, pasează null și lasă React să gestioneze mutabilitatea — tu doar apelezi metode pe el, cum ar fi inputRef.current?.focus().
4. Element implicitly has an 'any' type (Indexarea dinamică)
Ai un obiect de configurare și încerci să accesezi o cheie dinamică: const status = STATUS_MAP[userStatus]. TypeScript îți taie avântul: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type....
Pentru TypeScript, un string oarecare e prea larg ca să fie folosit ca cheie într-un obiect cu chei fixe. Soluția rapidă este să folosești un cast de tip: userStatus as keyof typeof STATUS_MAP. Un trade-off sincer aici: e extrem de rapid de scris, dar dacă userStatus vine dintr-un API extern și are o valoare ciudată la runtime, cast-ul ăsta te păcălește că ești safe când de fapt nu ești. Pentru date externe, e mai bine să faci o validare înainte.
5. Tipizarea pentru children și proprietăți de componente
Mulți devis foloseau React.FC<Props> pentru că includea automat proprietatea children. Din React 18, React.FC nu mai include children implicit, ceea ce e un lucru bun pentru că te obligă să fii explicit.
Dacă componenta ta primește copii, folosește React.PropsWithChildren<Props> sau adaugă manual children: React.ReactNode în interfața ta. Eu personal prefer să definesc un type sau o interface simplă pentru props fără să mai folosesc React.FC. Codul e mai curat și erorile din compilator sunt mult mai ușor de citit.
Care e eroarea de TypeScript care te-a scos cel mai tare din minți când ai început să scrii React strict?