Hai mai passato ore a debug perché un componente Vue riceveva una prop col tipo sbagliato? Oppure uno store Pinia cambiava struttura senza preavviso e l'applicazione esplodeva in produzione? Noi, di Meteora Web, sviluppiamo applicazioni Vue da anni, e abbiamo visto quanto costa l'assenza di tipi: ore perse, bug furtivi, clienti insoddisfatti. TypeScript non è una moda — è una garanzia che il codice si comporti come previsto. In questa guida ti mostriamo come portare TypeScript nei tuoi componenti Vue 3 e negli store Pinia, con esempi pronti da copiare.
Perché TypeScript con Vue fa risparmiare tempo e denaro?
TypeScript aggiunge un livello di controllo statico che intercetta errori prima del deploy. Se una prop riceve un numero invece di una stringa, o uno store restituisce un oggetto con una proprietà mancante, TypeScript te lo segnala in fase di sviluppo, non in produzione. Per una PMI, questo significa meno bug, meno tempo in debug e più fiducia nel rilascio. Noi lo applichiamo su ogni progetto Vue: dal piccolo gestionale interno a piattaforme multi-cliente. Il ritorno è immediato — codice più facile da mantenere e da far evolvere.
Errori comuni che TypeScript previene
- Prop passata con tipo errato (es. string invece di number) — TypeScript ti blocca subito.
- Emit senza controllo: se il componente figlio emette un evento con un payload sbagliato, il padre non lo riceve correttamente. TypeScript lo tipizza.
- Store con struttura fragile: cambiare un campo in un Pinia store senza aggiornare i componenti causava errori silenziosi. Con TypeScript il compilatore urlà.
- Ref e reactive non tipizzate: se dichiari
const x = ref([]), TypeScript deduceRef<never[]>e restringe le operazioni.
Come tipizzare le props di un componente Vue 3 con Composition API?
La Composition API permette di scrivere la logica in funzioni setup, ma le props vanno dichiarate e tipizzate. Ci sono due approcci: usare l'interfaccia dedicata o dichiararle inline. Noi preferiamo il primo, più leggibile e riutilizzabile.
Sponsored Protocol
Approccio con interfaccia
// components/UserCard.vue
<script setup lang="ts">
interface Props {
name: string;
age: number;
isActive?: boolean; // opzionale
}
const props = defineProps<Props>();
// Ora props.name è string, props.age è number, props.isActive è boolean | undefined
</script>
Con defineProps<Props>(), TypeScript controlla ogni utilizzo. Se in un altro componenti passi name={42}, il compilatore si arrabbia. Attenzione: defineProps con generics funziona solo con <script setup>. Se usi la Options API, devi usare il costrutto classico con PropType.
Sponsored Protocol
Props con valori di default
interface Props {
title: string;
count?: number;
}
const props = withDefaults(defineProps<Props>(), {
count: 0
});
// count è sempre number, anche se non passato
Come gestire gli emit tipizzati con TypeScript?
Anche gli eventi in uscita dal componente vanno tipizzati, per evitare che il padre riceva dati malformati. Si dichiarano con defineEmits.
// components/UserForm.vue
<script setup lang="ts">
interface Emits {
(e: 'submit', data: { name: string; email: string }): void;
(e: 'cancel'): void;
}
const emit = defineEmits<Emits>();
// Uso
emit('submit', { name: 'Mario', email: 'mario@example.com' }); // OK
emit('submit', { name: 123 }); // ERRORE: name deve essere string
</script>
Nota: con TypeScript 5.x puoi usare anche la sintassi defineEmits<{ submit: [data: { name: string; email: string }]; cancel: [] }>(). Scegli quella che preferisci.
Come tipizzare ref, reactive e computed?
Spesso vediamo codice come const items = ref([]) — TypeScript deduce Ref<never[]>, e poi non puoi fare items.value.push('test') perché si aspetta never. Bisogna dichiarare il tipo esplicitamente.
Sponsored Protocol
Ref tipizzata
import { ref } from 'vue';
const counter = ref<number>(0); // Ref<number>
const users = ref<User[]>([]); // User è un'interfaccia
interface User {
id: number;
name: string;
}
Reactive tipizzato
reactive() deduce il tipo dall'oggetto passato. Se vuoi tipizzare esplicitamente, usa un'interfaccia.
interface FormState {
email: string;
password: string;
remember: boolean;
}
const form = reactive<FormState>({
email: '',
password: '',
remember: false
});
Computed tipizzato
Il tipo viene dedotto automaticamente dal valore restituito dalla funzione. Ma se vuoi forzarlo (utile per generics), usa il generics.
const double = computed<number>(() => counter.value * 2);
Come tipizzare uno store Pinia con TypeScript?
Pinia è già nativamente supportato da TypeScript. La scelta migliore è usare la Option Store o Setup Store con tipi espliciti. Vediamo la Setup Store, che si integra perfettamente con Composition API.
Setup Store tipizzata
// stores/cart.ts
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';
interface CartItem {
productId: number;
name: string;
price: number;
quantity: number;
}
export const useCartStore = defineStore('cart', () => {
const items = ref<CartItem[]>([]);
const total = computed(() => {
return items.value.reduce((sum, item) => sum + item.price * item.quantity, 0);
});
function addItem(item: CartItem) {
const existing = items.value.find(i => i.productId === item.productId);
if (existing) {
existing.quantity += item.quantity;
} else {
items.value.push(item);
}
}
return { items, total, addItem };
});
In un componente:
Sponsored Protocol
const store = useCartStore();
store.addItem({ productId: 1, name: 'T-shirt', price: 25, quantity: 2 }); // OK
store.addItem({ productId: 'abc' }); // ERRORE
Tipizzare getters e azioni con generics avanzati
Se hai getter che restituiscono tipi dipendenti dallo stato, TypeScript li deduce automaticamente. Puoi anche dichiarare interfacce per l'intero store.
export type CartStore = ReturnType<typeof useCartStore>;
Utile per passare lo store come prop o injetto.
Cosa fare adesso: checklist per integrare TypeScript in un progetto Vue esistente
- Installa TypeScript e i plugin Vue:
npm install -D typescript @vue/tsconfige configuratsconfig.jsoncome da guida ufficiale. - Rinomina i file: da
.jsa.tse i componenti in.vueconlang="ts"nello script. - Tipizza le props: parti dai componenti più usati. Usa
defineProps<Props>(). - Tipizza gli store Pinia: definisci interfacce per i dati e aggiungi tipi a ref e reactive.
- Abilita strict mode: in
tsconfig.jsonimposta"strict": trueper massima sicurezza. - Esegui
vue-tsc --noEmit: per verificare che non ci siano errori di tipo prima del build. - Integra in CI: aggiungi un job di type-checking per evitare che codice non tipizzato vada in produzione.
Noi, di Meteora Web, abbiamo seguito questi passi su oltre una decina di progetti — il risultato è codice più stabile e revisioni più veloci. TypeScript non è un sovraccarico: è un investimento che ripaga al primo deploy senza sorprese.
Sponsored Protocol
Per approfondire l'ecosistema Vue 3 con Composition API, leggi la nostra guida pillar su Vue.js 3 e Composition API. Sui custom hook in React invece abbiamo un articolo separato, ma i concetti di tipizzazione sono simili.
Risorsa esterna: Documentazione ufficiale Vue su TypeScript.