Table of Contents
Typage des props Vue.js avec TypeScript et PropType
Introduction
Le typage des props dans Vue.js avec TypeScript represente l’une des fondations les plus importantes pour construire des applications robustes. Les props constituent le mecanisme principal de communication entre composants. Sans un typage adequat, vous vous exposez a des erreurs runtime difficiles a debugger.
TypeScript detecte les erreurs avant meme l’execution du code, ce qui reduit considerablement le temps de debug. Dans cet article, nous explorons les techniques de typage des props dans Vue 3.
Pourquoi le typage des props est essentiel
Detection precoce des erreurs
// Sans typage - erreurs decouvertes a l'execution
props: {
user: Object,
count: Number
}
Avec ce code, rien n’empeche de passer un objet avec une structure incorrecte.
Autocompletion et IntelliSense
interface User {
id: number;
name: string;
role: 'admin' | 'user' | 'guest';
}
// Votre IDE proposera id, name, role
props.user.name // Autocompletion disponible
Documentation vivante
Les types servent de documentation qui ne peut pas devenir obsolete. Contrairement aux commentaires, les types sont verifies par le compilateur.
defineProps vs props avec PropType
Options API avec PropType
import { defineComponent, PropType } from 'vue';
interface Product {
id: number;
name: string;
price: number;
}
export default defineComponent({
props: {
product: {
type: Object as PropType<Product>,
required: true
},
quantity: {
type: Number,
default: 1
},
tags: {
type: Array as PropType<string[]>,
default: () => []
}
},
setup(props) {
console.log(props.product.name);
return {};
}
});
Composition API avec defineProps generique
<script setup lang="ts">
interface Product {
id: number;
name: string;
price: number;
}
interface Props {
product: Product;
quantity?: number;
tags?: string[];
}
const props = defineProps<Props>();
</script>
Comparaison des deux approches
| Aspect | PropType (Options API) | defineProps generique |
|---|---|---|
| Syntaxe | Plus verbose | Plus concise |
| Validation runtime | Oui | Non par defaut |
| Valeurs par defaut | Dans l’objet props | Avec withDefaults |
| Inference de type | Bonne | Excellente |
Props required vs optional
Props obligatoires avec PropType
export default defineComponent({
props: {
user: {
type: Object as PropType<User>,
required: true
},
status: {
type: String as PropType<'active' | 'inactive'>,
required: true,
validator: (value: string) => ['active', 'inactive'].includes(value)
}
}
});
Props obligatoires avec defineProps
<script setup lang="ts">
interface Props {
user: User; // Obligatoire
status: 'active' | 'inactive'; // Obligatoire
title?: string; // Optionnelle
}
const props = defineProps<Props>();
</script>
Props optionnelles avec valeurs par defaut
<script setup lang="ts">
interface Props {
config?: { theme: 'light' | 'dark' };
itemsPerPage?: number;
}
const props = withDefaults(defineProps<Props>(), {
config: () => ({ theme: 'light' }),
itemsPerPage: 10
});
</script>
withDefaults pour les valeurs par defaut typees
Regles importantes
- Types primitifs : Definis directement
- Objets et tableaux : Fonction factory obligatoire
- Types union : Valeur par defaut du bon type
<script setup lang="ts">
type ButtonVariant = 'primary' | 'secondary' | 'danger';
interface Props {
variant?: ButtonVariant;
disabled?: boolean;
options?: Record<string, unknown>;
}
const props = withDefaults(defineProps<Props>(), {
variant: 'primary',
disabled: false,
options: () => ({})
});
</script>
Typage des emits avec defineEmits
Syntaxe basee sur les types
<script setup lang="ts">
interface User {
id: number;
name: string;
}
const emit = defineEmits<{
(e: 'update', value: string): void;
(e: 'select', user: User): void;
(e: 'cancel'): void;
}>();
function handleSelect(selectedUser: User) {
emit('select', selectedUser);
}
</script>
Syntaxe alternative (Vue 3.3+)
<script setup lang="ts">
const emit = defineEmits<{
update: [value: string];
select: [user: User];
cancel: [];
}>();
</script>
Props de type fonction
<script setup lang="ts">
interface Props {
onClick?: () => void;
formatter?: (value: number) => string;
validator?: (value: string) => boolean;
}
const props = withDefaults(defineProps<Props>(), {
onClick: () => {},
formatter: (value) => value.toString(),
validator: () => true
});
</script>
Props fonction avec types generiques
<script setup lang="ts" generic="T">
interface Props {
items: T[];
keyExtractor: (item: T) => string | number;
filterFn?: (item: T) => boolean;
}
const props = defineProps<Props>();
const filteredItems = computed(() => {
if (props.filterFn) {
return props.items.filter(props.filterFn);
}
return props.items;
});
</script>
Props de type objet complexe
Interfaces imbriquees
<script setup lang="ts">
interface Address {
street: string;
city: string;
}
interface User {
id: number;
firstName: string;
lastName: string;
contact: { email: string; address?: Address };
preferences: { theme: 'light' | 'dark' };
}
interface Props {
user: User;
editable?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
editable: false
});
const fullName = computed(() =>
`${props.user.firstName} ${props.user.lastName}`
);
</script>
Utilisation de types utilitaires
<script setup lang="ts">
interface Product {
id: number;
name: string;
price: number;
stock: number;
}
type ProductPreview = Pick<Product, 'id' | 'name' | 'price'>;
type ProductUpdate = Partial<Omit<Product, 'id'>>;
interface Props {
product?: Product;
preview?: ProductPreview;
mode: 'view' | 'edit' | 'create';
}
const props = defineProps<Props>();
</script>
Props avec types union discrimines
<script setup lang="ts">
type LoadingState = { status: 'loading' };
type SuccessState<T> = { status: 'success'; data: T };
type ErrorState = { status: 'error'; error: string };
type DataState<T> = LoadingState | SuccessState<T> | ErrorState;
interface Props {
userState: DataState<User>;
}
const props = defineProps<Props>();
function renderContent() {
switch (props.userState.status) {
case 'loading': return 'Chargement...';
case 'success': return props.userState.data.name;
case 'error': return `Erreur: ${props.userState.error}`;
}
}
</script>
Tableau comparatif Options API vs Composition API
| Fonctionnalite | Options API | Composition API |
|---|---|---|
| Definition | props: { ... } | defineProps<T>() |
| Typage | PropType<T> | Interface generique |
| Valeurs par defaut | default: value | withDefaults() |
| Validation runtime | validator: fn | Non disponible |
| Required | required: true | Propriete non-optionnelle |
| Acces | this.propName | props.propName |
Bonnes pratiques
1. Toujours definir des interfaces explicites
// Mauvais
const props = defineProps<{
user: { id: number; name: string };
}>();
// Bon
interface User { id: number; name: string; }
const props = defineProps<{ user: User }>();
2. Utiliser des fichiers de types partages
// types/index.ts
export interface User { id: number; name: string; }
// Composant
import type { User } from '@/types';
3. Preferer les types stricts
// Mauvais
interface Props { status: string; }
// Bon
interface Props { status: 'pending' | 'active' | 'completed'; }
4. Documenter avec JSDoc
interface Props {
/** Configuration du tableau @default { striped: true } */
config?: TableConfig;
}
5. Regrouper les props optionnelles
// Mauvais
interface Props { title?: string; icon?: string; color?: string; }
// Bon
interface CardConfig { title?: string; icon?: string; }
interface Props { content: CardConfig; }
6. Utiliser readonly
interface Props {
readonly config: Readonly<Config>;
readonly items: ReadonlyArray<Item>;
}
Pieges courants a eviter
1. Oublier la fonction factory
// ERREUR
withDefaults(defineProps<Props>(), { items: [] });
// CORRECT
withDefaults(defineProps<Props>(), { items: () => [] });
2. Destructurer les props sans toRefs
// ERREUR - perte de reactivite
const { user } = props;
// CORRECT
const { user } = toRefs(props);
3. Modifier les props directement
// ERREUR
props.user.name = 'Nouveau nom';
// CORRECT
emit('update:user', { ...props.user, name: 'Nouveau nom' });
4. Types trop larges
// ERREUR
interface Props { data: any; callback: Function; }
// CORRECT
interface Props { data: UserData; callback: (result: Result) => void; }
5. Ignorer les props undefined
// ERREUR
const fullName = props.user.firstName + ' ' + props.user.lastName;
// CORRECT
const fullName = computed(() =>
props.user ? `${props.user.firstName} ${props.user.lastName}` : ''
);
Conclusion
Le typage des props dans Vue.js avec TypeScript est une competence fondamentale pour construire des applications robustes :
- PropType pour l’Options API offre une validation runtime
- defineProps generique avec la Composition API fournit une syntaxe plus concise
- withDefaults permet de definir des valeurs par defaut type-safe
- defineEmits assure le typage complet de la communication parent-enfant
Les bonnes pratiques couvertes - interfaces explicites, types stricts, documentation JSDoc - vous aideront a maintenir un code de qualite. En evitant les pieges courants, vous reduirez considerablement les bugs.
L’investissement dans un typage rigoureux se traduit par une meilleure autocompletion, une documentation vivante et une detection precoce des erreurs. C’est un element cle pour des applications Vue.js professionnelles et maintenables.
In-Article Ad
Dev Mode
Mahmoud DEVO
Senior Full-Stack Developer
I'm a passionate full-stack developer with 10+ years of experience building scalable web applications. I write about Vue.js, Node.js, PostgreSQL, and modern DevOps practices.
Enjoyed this article?
Subscribe to get more tech content delivered to your inbox.
Related Articles
Pinia State Management: The Complete Vue 3 Guide
Master Pinia, the official Vue 3 state management library. Learn stores, actions, getters, plugins, and best practices.
Définition de l'état de magasin avec TypeScript et Vue.js
Apprenez à définir et typer l'état de votre store Vue avec TypeScript. Interfaces, génériques reactive() et mutations typées pour un code robuste.
Créer une application météo avec Vue.js et Vuex : Une approc
Voici une proposition de meta description qui correspond aux exigences : "Découvrez comment créer une application Vue.js responsive et sécurisée avec un systèm