Prevenir les bugs
Les enchantements defensifs : TypeScript, linting, tests.
8.1Bouclier 1 : TypeScript (le compilateur te protege)
TypeScript attrape une ENORME categorie de bugs avant meme que tu executes ton code. C'est comme avoir un scanner qui detecte les pieges avant que tu marches dessus.
// Sans TypeScript : bug silencieux en production
function greet(user) {
return "Hello " + user.name // Si user est null -> crash en prod !
}
// Avec TypeScript : erreur a l'ecriture
function greet(user: User | null) {
return "Hello " + user.name
// ^^^^ Object is possibly 'null'
// TypeScript te FORCE a gerer le cas null
}
// Le fix que TypeScript te suggere :
function greet(user: User | null) {
if (!user) return "Hello visiteur"
return "Hello " + user.name // OK, user est forcement User ici
}
// Activer le mode strict dans tsconfig.json
// "strict": true <- attrape 10x plus de bugs8.2Bouclier 2 : ESLint (l'inspecteur de code)
ESLint detecte les patterns problematiques dans ton code. Il ne verifie pas les types (ca c'est TypeScript), mais les mauvaises habitudes.
// eslint-disable-next-line @typescript-eslint/no-unused-vars
// -> detecte les imports et variables non utilises
// eslint: react-hooks/exhaustive-deps
// -> detecte les dependances manquantes dans useEffect
useEffect(() => {
fetchUser(userId) // ESLint : userId manque dans les deps
}, []) // Warning !
// eslint: react-hooks/rules-of-hooks
// -> detecte les hooks appeles conditionnellement
if (loggedIn) {
const [user, setUser] = useState() // ERREUR : hook conditionnel
}
// eslint: no-console
// -> supprime les console.log oublies avant la prod8.3Bouclier 3 : Le pattern "guard clause"
Au lieu de faire des if/else imbriques, traite les cas d'erreur En premier. C'est un enchantement simple mais redoutablement efficace.
// AVANT : if/else imbrique = jungle
async function getUser(userId: string) {
if (userId) {
const response = await fetch(`/api/users/${userId}`)
if (response.ok) {
const data = await response.json()
if (data) {
return data
} else {
throw new Error("No data")
}
} else {
throw new Error("Fetch failed")
}
} else {
throw new Error("No userId")
}
}
// APRES : guard clauses = clair et lineaire
async function getUser(userId: string) {
if (!userId) throw new Error("No userId")
const response = await fetch(`/api/users/${userId}`)
if (!response.ok) throw new Error(`Fetch failed: ${response.status}`)
const data = await response.json()
if (!data) throw new Error("No data")
return data
}8.4Bouclier 4 : Les valeurs par defaut
// Props avec default
interface CardProps {
title: string
variant?: 'primary' | 'secondary'
items?: string[]
}
function Card({ title, variant = 'primary', items = [] }: CardProps) {
// variant et items ne seront JAMAIS undefined
return (
<div className={variant === 'primary' ? 'bg-blue-500' : 'bg-gray-500'}>
<h2>{title}</h2>
{items.map(item => <p key={item}>{item}</p>)}
</div>
)
}
// Nullish coalescing : valeur de secours
const name = user?.name ?? "Inconnu"
const count = data?.total ?? 0
const items = response?.results ?? []8.5Bouclier 5 : Le error boundary React
// app/error.tsx - Error Boundary automatique Next.js
'use client'
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string }
reset: () => void
}) {
return (
<div className="text-center py-20">
<h2 className="text-2xl font-bold mb-4">Oups, quelque chose s'est mal passe</h2>
<p className="text-gray-600 mb-6">{error.message}</p>
<button
onClick={reset}
className="bg-blue-500 text-white px-6 py-2 rounded-lg"
>
Reessayer
</button>
</div>
)
}
// Ce fichier attrape TOUTES les erreurs de la route.
// Au lieu d'un ecran blanc, l'utilisateur voit un message propre.8.6La checklist pre-commit
Avant chaque commit
- Pas de console.log restant ?
- Pas de TypeScript error (rouge souligne) ?
- ESLint passe sans warning ?
- Le build passe (
npm run build) ? - Les cas edge sont geres (null, undefined, tableau vide) ?
- Pas de donnees en dur qui devraient etre en variable d'environnement ?