8 luglio 2026
TypeScript in React: tipizzare stato, props e funzioni senza impazzire
Una guida pratica per portare un progetto React “classico” in TypeScript, con scelte di typing sensate e componenti più riusabili.
Integrare TypeScript in React non significa riempire il codice di tipi ovunque: significa mettere i tipi nei punti giusti. In questo articolo vediamo un flusso di lavoro concreto: creare un progetto React con Vite + TypeScript, tipizzare dati e utility, gestire useState con generics quando serve, e definire props (anche funzione) in modo pulito e riusabile.
Perché TypeScript in React fa davvero la differenza
In un progetto React reale, gli errori più fastidiosi raramente sono “sintassi sbagliata”. Di solito sono:
- stato che cambia forma nel tempo (stringhe che diventano boolean, array che contengono
any), - props passate male tra componenti,
- callback con parametri non chiari (o non documentati),
- refactor che rompono comportamenti lontani dal punto in cui stai lavorando.
TypeScript interviene esattamente qui: ti aiuta a rendere espliciti i contratti (stato, funzioni, props) e a farli rispettare automaticamente dall’editor e dalla build.
Parti bene: crea il progetto già “TypeScript-ready”
Aggiungere TypeScript in un progetto React esistente è possibile, ma richiede una serie di passaggi (dipendenze, configurazioni, rinomina file, dichiarazioni dei tipi) che aumentano il rischio di attriti inutili.
La via più pulita è creare direttamente un progetto con Vite e variante TypeScript:
npm create vite@latest
# Framework: React
# Variant: TypeScript
npm install
Così ti ritrovi già:
typescript,@types/reacte@types/react-dom,- configurazioni TS pronte (spesso con
tsconfigsuddivisi per app/node), - ESLint e toolchain compatibili.
In pratica: meno “config fatigue”, più tempo sul codice.
Tipizzare i dati: array di stringhe e array di oggetti
1) Array di stringhe
Un file che esporta parole del gioco è il caso più semplice: un array di stringhe.
export const words: string[] = [
"javascript",
"typescript",
"react",
]
Qui TypeScript spesso inferisce già il tipo, ma l’annotazione esplicita è utile quando vuoi rendere il contratto inequivocabile.
2) Array di oggetti: inline vs type dedicato
Quando hai una lista di “linguaggi” con proprietà ripetute, puoi scegliere due strade:
Inline (veloce, ma meno riusabile):
export const languages: {
name: string
backgroundColor: string
color: string
}[] = [
{ name: "JavaScript", backgroundColor: "#f7df1e", color: "#000" },
]
Type dedicato (consigliato):
type Language = {
name: string
backgroundColor: string
color: string
}
export const languages: Language[] = [
{ name: "JavaScript", backgroundColor: "#f7df1e", color: "#000" },
]
Il vantaggio del type dedicato emerge subito quando:
- lo riutilizzi in più file,
- lo usi come tipo di prop,
- vuoi mantenere un’unica fonte di verità.
Tipizzare le utility: funzioni con return chiaro e niente any
Due tipologie di funzioni ricorrono spesso:
- funzioni che ritornano un valore deterministico (es. una stringa),
- funzioni che scelgono casualmente da una lista.
Return type esplicito
Anche se TS può inferire, dichiarare il return type è un ottimo esercizio di chiarezza:
export function getRandomWord(): string {
// ...
return "react"
}
Parametri tipizzati (il primo argine contro any)
Quando una funzione riceve un parametro e TypeScript non riesce a dedurlo, rischi any implicito (dipende dalle impostazioni). Tipizza subito:
export function getFarewellText(language: string): string {
const options: string[] = [
`Addio, ${language}!`,
`${language} è stato rimosso dalla timeline.`,
]
const randomIndex: number = Math.floor(Math.random() * options.length)
return options[randomIndex]
}
Refactor utile: estrai getRandomIndex
Se ripeti lo stesso calcolo in più punti, estrarlo migliora leggibilità e testabilità.
function getRandomIndex(arr: string[]): number {
return Math.floor(Math.random() * arr.length)
}
Poi lo riusi:
export function getRandomWord(): string {
return words[getRandomIndex(words)]
}
export function getFarewellText(language: string): string {
const options: string[] = [/* ... */]
return options[getRandomIndex(options)]
}
Nota: qui getRandomIndex accetta string[] perché è il caso d’uso attuale. Se vuoi renderla più generica in futuro, puoi passare a un generic T[] (ma solo quando serve davvero).
Tipizzare useState: quando l’inferenza basta e quando no
useState spesso inferisce bene il tipo dal valore iniziale. Se inizializzi con una stringa, avrai uno state di tipo string.
Il problema tipico nasce quando inizializzi con un array vuoto:
const [guessedLetters, setGuessedLetters] = useState([])
Un array vuoto non dà abbastanza informazioni, e rischi un any[] (o un tipo troppo largo). Qui conviene usare i generics di useState.
Esempio: stringa
const [currentWord, setCurrentWord] = useState<string>(() => getRandomWord())
Esempio: array di stringhe
const [guessedLetters, setGuessedLetters] = useState<string[]>([])
Questa singola scelta impedisce un’intera classe di bug: nessuno potrà più fare setGuessedLetters([1, 2, 3]) senza che TypeScript lo blocchi.
Props tipizzate: inline, type, import e riuso
Quando inizi a spezzare l’interfaccia in componenti, la tipizzazione delle props diventa la parte più “pagante”.
Props inline (ok per componenti piccoli)
function Keyboard(props: { onGuess: (letter: string) => void }) {
// ...
}
Props con type dedicato (preferibile)
type KeyboardProps = {
onGuess: (letter: string) => void
}
function Keyboard({ onGuess }: KeyboardProps) {
// ...
}
Importare tipi da altri file
Se hai già un type come Language in languages.ts, puoi riutilizzarlo come prop type in un componente “Badge” o “Status”:
import type { Language } from "./languages"
type LanguageBadgeProps = {
language: Language
}
Questo approccio evita duplicazioni e mantiene la coerenza quando la struttura dati evolve.
Il pattern più comune: tipizzare le function props
In React passiamo funzioni continuamente: handler di click, callback di submit, setter “incapsulati”. La regola pratica è semplice:
- tipizza i parametri in ingresso,
- tipizza il return (spesso
void).
type GuessHandler = (letter: string) => void
type KeyboardProps = {
onGuess: GuessHandler
}
Se domani onGuess dovrà accettare anche un contesto (es. la posizione o la sorgente dell’input), TypeScript ti guiderà nel refactor perché ogni punto d’uso verrà aggiornato in modo forzato.
In sintesi: metti i tipi dove riducono davvero l’entropia
TypeScript in React non è un esercizio di burocrazia: è un modo per costruire componenti e funzioni con contratti espliciti.
Le priorità che danno più ritorno sono:
- State: usa i generics di
useStatequando l’inferenza è ambigua (es. array vuoti). - Dati e modelli: preferisci type dedicati per array di oggetti riusabili.
- Utility: tipi su parametri e return (e refactor delle parti ripetute).
- Props e callback: tipizza bene le function props, perché sono la spina dorsale della comunicazione tra componenti.
Con questi pochi punti fermi, il codice diventa più robusto, il refactor più sicuro e l’editor smette di “indovinare” cosa intendevi.