Next.js + TypeScript : configuration et bonnes pratiques (2026)
TypeScript est incontournable dans un projet Next.js sérieux. Ce guide vous montre comment configurer TypeScript dans Next.js, typer les composants, les props, les Server Actions et les APIs pour un code robuste et maintenable.
TypeScript est activé par défaut dans Next.js depuis la version 13. En 2026, développer un projet Next.js sans TypeScript est une erreur — les gains en maintenabilité et en sécurité de type valent largement le coût d’apprentissage. Voici la configuration optimale et les patterns à connaître.
Configuration tsconfig.json pour Next.js
Next.js génère un tsconfig.json optimal. Les options les plus importantes :
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"strict": true, // Activez TOUJOURS strict mode
"noUncheckedIndexedAccess": true, // Sécurise l'accès aux tableaux
"moduleResolution": "bundler",
"paths": {
"@/*": ["./src/*"] // Import alias
}
}
}
Le strict: true active 8 règles TypeScript strictes dont noImplicitAny et strictNullChecks. Ne le désactivez jamais — il protège des bugs les plus courants.
Typer les composants et les props
// Interface pour les props
interface ArticleCardProps {
title: string;
excerpt: string;
publishedAt: Date;
author: { name: string; avatar?: string };
tags: string[];
onTagClick?: (tag: string) => void; // prop optionnelle
}
export function ArticleCard({ title, excerpt, author, onTagClick }: ArticleCardProps) {
return (
<article>
<h2>{title}</h2>
<p>{excerpt}</p>
{author.avatar && <img src={author.avatar} alt={author.name} />}
</article>
);
}
Typer les routes et les paramètres
// app/blog/[slug]/page.tsx
interface PageProps {
params: { slug: string };
searchParams: { page?: string; sort?: 'asc' | 'desc' };
}
export default async function ArticlePage({ params, searchParams }: PageProps) {
const article = await getArticle(params.slug);
const page = parseInt(searchParams.page ?? '1', 10);
// ...
}
Typer les appels fetch et les données API
interface Article {
id: number;
title: string;
slug: string;
content: string;
publishedAt: string; // ISO string depuis l'API
author: { id: number; name: string };
}
async function getArticle(slug: string): Promise<Article> {
const res = await fetch(`/api/articles/${slug}`);
if (!res.ok) throw new Error(`Article not found: ${slug}`);
return res.json() as Promise<Article>;
}
// Avec Zod pour la validation runtime
import { z } from 'zod';
const ArticleSchema = z.object({
id: z.number(),
title: z.string().min(1).max(200),
slug: z.string(),
});
type Article = z.infer<typeof ArticleSchema>;
const article = ArticleSchema.parse(await res.json()); // valide ET type
Typer les Server Actions
"use server";
import { z } from 'zod';
const CreateArticleSchema = z.object({
title: z.string().min(1, 'Le titre est requis'),
content: z.string().min(50),
});
type ActionResult = { success: true; id: number } | { success: false; errors: Record<string, string[]> };
export async function createArticle(formData: FormData): Promise<ActionResult> {
const raw = { title: formData.get('title'), content: formData.get('content') };
const result = CreateArticleSchema.safeParse(raw);
if (!result.success) return { success: false, errors: result.error.flatten().fieldErrors };
const article = await db.article.create({ data: result.data });
return { success: true, id: article.id };
}
Utilitaires TypeScript utiles
// Partial — toutes les propriétés optionnelles
type UpdateArticle = Partial<Article>;
// Pick — seulement certaines propriétés
type ArticleSummary = Pick<Article, 'id' | 'title' | 'slug'>;
// Omit — tout sauf certaines propriétés
type CreateArticle = Omit<Article, 'id' | 'publishedAt'>;
// Record — objet avec clés et valeurs typées
type LocaleMessages = Record<'fr' | 'en' | 'es', string>;
// NonNullable — exclut null et undefined
type SafeUser = NonNullable<User | null>; // = User
ESLint TypeScript
Next.js inclut ESLint préconfigurée. Ajoutez les règles TypeScript strictes dans .eslintrc.json :
{
"extends": ["next/core-web-vitals", "next/typescript"],
"rules": {
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/prefer-nullish-coalescing": "warn"
}
}