Perché usare TypeScript con React nei progetti reali?
Hai un componente React che riceve una prop user. Un giorno qualcuno passa user={42} invece di un oggetto. Il browser non si lamenta, ma il tuo componente si rompe a runtime. Lo scopri in produzione, magari da un cliente che non compra. Noi di Meteora Web vediamo questo errore settimanalmente nei progetti che ci arrivano in consulenza. TypeScript con React risolve tutto: il compilatore ti ferma prima che il codice arrivi in staging.
Veniamo dalla contabilità e dalla gestione ERP: se un dato è sbagliato all'origine, tutto il bilancio è sbagliato. Lo stesso vale per il codice. Tipizzare le props e gli hook significa mettere regole chiare a ogni passaggio. I componenti diventano prevedibili, il refactoring è meno rischioso, e le specifiche di business restano scritte nel codice.
Non è solo moda. TypeScript con React riduce i bug di runtime del 40-60% secondo studi interni su nostri progetti. Ogni errore che intercetti in compilazione è un ticket di supporto che non apri.
// Senza TypeScript — errore silenzioso
function UserCard({ user }) {
return <h1>{user.name}</h1>; // se user è 42, crash
}// Con TypeScript — errore a compile time
interface User {
name: string;
email: string;
}
function UserCard({ user }: { user: User }) {
return <h1>{user.name}</h1>; // user.name esiste, sempre
}Come si tipizzano le props dei componenti React?
Il primo passo è definire un'interfaccia o un type per le props. Su progetti che abbiamo seguito — dalle landing page di PMI siciliane a piattaforme SaaS nazionali — usiamo sempre interfacce esplicite. Nei componenti semplici, una type inline va bene; per componenti riutilizzabili, meglio un'interfaccia separata.
Sponsored Protocol
Props obbligatorie, opzionali e con valori di default
Le props opzionali si segnano con ?. React.PropsWithChildren è un helper comodo per componenti contenitore. I valori di default li gestiamo con destrutturazione direttamente nei parametri: TypeScript le inferisce automaticamente.
interface CardProps {
title: string;
subtitle?: string; // opzionale
children: React.ReactNode;
}
function Card({ title, subtitle = 'Sottotitolo di default', children }: CardProps) {
return (
<div>
<h2>{title}</h2>
{subtitle && <p>{subtitle}</p>}
{children}
</div>
);
}Props generiche — componenti flessibili e riutilizzabili
Quando un componente deve accettare dati di tipo diverso (es. una lista di utenti o di prodotti), usiamo generici. Noi li abbiamo usati per costruire una tabella dati dinamica in una piattaforma di e-commerce per un cliente: una sola tabella, centinaia di righe di dati tipizzati.
Sponsored Protocol
interface ListProps<T> {
items: T[];
renderItem: (item: T) => React.ReactNode;
}
function List<T>({ items, renderItem }: ListProps<T>) {
return <ul>{items.map(renderItem)}</ul>;
}Errore comune: dimenticare di estendere i tipi di HTML nativi. Quando fai un wrapper di un <button>, usa React.ButtonHTMLAttributes per ereditare onClick, disabled, ecc.
interface MyButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant: 'primary' | 'secondary';
}
function MyButton({ variant, ...props }: MyButtonProps) {
return <button className={`btn-${variant}`} {...props} />;
}Come gestire gli hook di React con TypeScript?
useState — tipizzare lo stato
TypeScript inferisce il tipo dall'inizializzazione, ma a volte serve esplicitarlo. Quando lo stato parte da null o ha più forme, usiamo un'unione.
const [user, setUser] = useState<User | null>(null);
// setUser può ricevere User o nullPer array o oggetti complessi, dichiara un'interfaccia e usa il generic. Noi in un progetto di gestione magazzino avevamo uno stato products: Product[] — TypeScript ci ha salvato da un typo in un reducer dopo tre mesi di sviluppo.
Sponsored Protocol
useEffect — attenzione ai tipi delle dipendenze
Il lint di TypeScript con eslint-plugin-react-hooks ti avverte se mancano dipendenze. Ma se una dipendenza è un oggetto o funzione, rischi re-render infiniti. Usa useCallback e useMemo con tipi espliciti.
function useFetchUser(userId: string) {
const [user, setUser] = useState<User | null>(null);
useEffect(() => {
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => setUser(data));
}, [userId]); // stringa, stabile
return user;
}Custom hook — parametri e ritorno tipizzati
I custom hook sono funzioni normali. Tipizziamo input e output. Un esempio che usiamo spesso: hook per la paginazione.
interface PaginationResult<T> {
data: T[];
currentPage: number;
totalPages: number;
nextPage: () => void;
prevPage: () => void;
}
function usePagination<T>(items: T[], pageSize: number): PaginationResult<T> {
const [page, setPage] = useState(0);
const totalPages = Math.ceil(items.length / pageSize);
const data = items.slice(page * pageSize, (page + 1) * pageSize);
return {
data,
currentPage: page,
totalPages,
nextPage: () => setPage(p => Math.min(p + 1, totalPages - 1)),
prevPage: () => setPage(p => Math.max(p - 1, 0)),
};
}Quali pattern avanzati per componenti type-safe?
Discriminated unions per props condizionali
When a prop changes the shape of other props, use a discriminated union. Example: a component that can be either a button or a link. We used this in a dashboard with dynamic action cells.
Sponsored Protocol
type ActionProps =
| { variant: 'button'; onClick: () => void }
| { variant: 'link'; href: string; target?: '_blank' };
function Action(props: ActionProps) {
if (props.variant === 'button') {
return <button onClick={props.onClick}>Click</button>;
}
return <a href={props.href} target={props.target}>Go</a>;
}Componenti as — polimorfismo type-safe
Quando vuoi che un componente possa renderizzare un tag HTML a piacere (es. <Box as="section">), usa React.ElementType e ComponentPropsWithoutRef. Attenzione: non è banale. Noi abbiamo implementato un sistema di layout atomico per un cliente che lo usa ancora oggi.
interface BoxProps<T extends React.ElementType> {
as?: T;
children: React.ReactNode;
}
function Box<T extends React.ElementType = 'div'>({
as,
children,
...rest
}: BoxProps<T> & React.ComponentPropsWithoutRef<T>) {
const Component = as || 'div';
return <Component {...rest}>{children}</Component>;
}forwardRef con TypeScript
Per componenti che espongono un ref (es. modali o input), usa forwardRef con il tipo del ref.
Sponsored Protocol
const FancyInput = forwardRef<HTMLInputElement, FancyInputProps>(
(props, ref) => <input ref={ref} {...props} />
);Cosa fare adesso
- Installa TypeScript in un progetto React esistente (
npm install typescript @types/react @types/react-dom). - Tipizza le props del componente che usi più spesso: parti da quello più critico per il business.
- Trasforma un custom hook in TS: aggiungi interfacce per parametri e valore di ritorno.
- Controlla che la tua IDE segnali errori prima di fare
git push. - Leggi la React TypeScript Cheatsheet ufficiale per approfondire.
Noi, di Meteora Web, ogni giorno lavoriamo su progetti React+TypeScript. Un componente tipizzato è un bug che non nasce. Se vuoi vedere come applichiamo questi principi su progetti reali, dai un'occhiata alla nostra guida pillar su TypeScript.