Cours Vibe coding avec l'IA · Module 2: Configuration de Next.js

TypeScript pour Next.js - Types et interfaces

11 min de lecture
Dans cette leçon15

Ton projet Next.js utilise TypeScript - c'est comme JavaScript avec des super-pouvoirs ! Il ajoute des types qui aident à éviter les bugs.

Qu'est-ce que TypeScript ?

TypeScript est JavaScript avec un système de types. Le code TypeScript est compilé en JavaScript classique.

1// JavaScript - sans types
2function greet(name) {
3  return "Hello " + name;
4}
5
6greet(123);  // Fonctionne, mais erreur de logique !
1// TypeScript - avec types
2function greet(name: string) {
3  return "Hello " + name;
4}
5
6greet(123);  // Erreur : Argument of type 'number' not assignable to 'string'

Pourquoi TypeScript ?

  • Sécurité - erreurs détectées avant l'exécution
  • Intellisense - suggestions de l'éditeur
  • Documentation - les types sont de la documentation
  • Refactoring - changements de code plus faciles
  • Par défaut dans Next.js - create-next-app crée un projet TypeScript, sauf si tu choisis autre chose

Types de base en TypeScript

1. Types primitifs

1// String
2const shipName: string = "Black Pearl";
3const captain: string = "Jack Sparrow";
4
5// Number
6const crewCount: number = 42;
7const treasureValue: number = 1000000;
8
9// Boolean
10const isSailing: boolean = true;
11const hasKraken: boolean = false;
12
13// Undefined et Null
14let treasure: number | undefined;  // Peut être un number ou undefined
15let kraken: null | object = null;

2. Tableaux

1// Tableau de nombres
2const levels: number[] = [1, 2, 3, 4, 5];
3
4// Tableau de chaînes
5const pirates: string[] = ["Jack", "Anne", "Blackbeard"];
6
7// Syntaxe alternative
8const ships: Array<string> = ["Pearl", "Dutchman"];
9
10// Tableau mixte (union type)
11const mixed: (string | number)[] = ["Jack", 42, "Anne", 7];

3. Objets

1// Type inline
2const pirate: {
3  name: string;
4  level: number;
5  ship: string;
6} = {
7  name: "Jack Sparrow",
8  level: 10,
9  ship: "Black Pearl"
10};
11
12// Problème : on doit répéter les types pour chaque pirate !

Interfaces - Définir des structures

Une interface est comme un plan pour les objets - elle définit la forme des données.

Interface de base

1interface Pirate {
2  name: string;
3  level: number;
4  ship: string;
5  isActive: boolean;
6}
7
8// Maintenant on peut réutiliser ce type plusieurs fois
9const jack: Pirate = {
10  name: "Jack Sparrow",
11  level: 10,
12  ship: "Black Pearl",
13  isActive: true
14};
15
16const anne: Pirate = {
17  name: "Anne Bonny",
18  level: 8,
19  ship: "Revenge",
20  isActive: true
21};
22
23// Tableau de pirates
24const crew: Pirate[] = [jack, anne];

Propriétés optionnelles

Le signe ? signifie que la propriété est optionnelle :

1interface Ship {
2  name: string;
3  captain: string;
4  crewCount: number;
5  treasure?: number;  // Optionnel - peut être undefined
6  flag?: string;      // Optionnel
7}
8
9const pearl: Ship = {
10  name: "Black Pearl",
11  captain: "Jack Sparrow",
12  crewCount: 42
13  // treasure et flag sont optionnels - on peut les omettre
14};
15
16const dutchman: Ship = {
17  name: "Flying Dutchman",
18  captain: "Davy Jones",
19  crewCount: 100,
20  treasure: 1000000  // Cette fois on a un trésor
21};

Interface pour les fonctions

1interface GreetFunction {
2  (name: string): string;
3}
4
5const greetPirate: GreetFunction = (name) => {
6  return `Ahoy, ${name}!`;
7};
8
9console.log(greetPirate("Jack"));  // "Ahoy, Jack!"

Type vs Interface

TypeScript propose deux façons de définir des types : type et interface.

Type

1type Pirate = {
2  name: string;
3  level: number;
4};
5
6type Ship = {
7  name: string;
8  captain: string;
9};

Interface

1interface Pirate {
2  name: string;
3  level: number;
4}
5
6interface Ship {
7  name: string;
8  captain: string;
9}

Différences :

FonctionnalitéTypeInterface
ExtensionOui (avec &)Oui (avec extends)
Union typesOuiNon
PrimitifsOuiNon
RéouvertureNonOui

Quand les utiliser :

  • Interface - pour les objets, les props de composants React
  • Type - pour les union types, les primitifs, les types complexes

Union Types - Le type « ou »

1// Le type peut être string OU number
2type ID = string | number;
3
4const userId1: ID = "abc123";  // OK
5const userId2: ID = 42;        // OK
6
7// Le type peut être plusieurs valeurs précises
8type Status = "pending" | "sailing" | "docked" | "sunk";
9
10const shipStatus: Status = "sailing";  // OK
11const wrongStatus: Status = "flying";  // Erreur !

Types dans les composants React

Interface de props

1// Définir les types des props
2interface ButtonProps {
3  text: string;
4  onClick: () => void;
5  variant?: "primary" | "secondary";  // Optionnel
6}
7
8// Composant avec des props typées
9function Button({ text, onClick, variant = "primary" }: ButtonProps) {
10  return (
11    <button
12      onClick={onClick}
13      className={`btn btn-${variant}`}
14    >
15      {text}
16    </button>
17  );
18}
19
20// Utilisation
21<Button text="Ahoy!" onClick={() => console.log("Clicked!")} />

Prop children

1interface CardProps {
2  title: string;
3  children: React.ReactNode;  // Type pour children
4}
5
6function Card({ title, children }: CardProps) {
7  return (
8    <div className="card">
9      <h2>{title}</h2>
10      {children}
11    </div>
12  );
13}
14
15// Utilisation
16<Card title="Pirate Card">
17  <p>This is content inside card</p>
18</Card>

Gestionnaires d'événements

1interface FormProps {
2  onSubmit: (email: string) => void;
3}
4
5function Form({ onSubmit }: FormProps) {
6  const handleSubmit = (e: React.SyntheticEvent<HTMLFormElement>) => {
7    e.preventDefault();
8    const formData = new FormData(e.currentTarget);
9    const email = formData.get("email") as string;
10    onSubmit(email);
11  };
12
13  return (
14    <form onSubmit={handleSubmit}>
15      <input name="email" type="email" />
16      <button type="submit">Join crew</button>
17    </form>
18  );
19}

Generics - Types universels

Les generics te permettent de créer des composants/fonctions qui fonctionnent avec différents types.

1// Fonction générique
2function getFirstItem<T>(items: T[]): T | undefined {
3  return items[0];
4}
5
6const firstNumber = getFirstItem([1, 2, 3]);  // number | undefined
7const firstString = getFirstItem(["a", "b"]);  // string | undefined
8
9// Interface générique
10interface Response<T> {
11  data: T;
12  status: number;
13  message: string;
14}
15
16interface Pirate {
17  name: string;
18  level: number;
19}
20
21const pirateResponse: Response<Pirate> = {
22  data: { name: "Jack", level: 10 },
23  status: 200,
24  message: "Success"
25};
26
27const stringResponse: Response<string> = {
28  data: "Hello",
29  status: 200,
30  message: "OK"
31};

TypeScript dans Next.js - Exemples pratiques

Composant de page

1// app/pirates/page.tsx
2interface Pirate {
3  id: number;
4  name: string;
5  ship: string;
6  level: number;
7}
8
9export default function PiratesPage() {
10  const pirates: Pirate[] = [
11    { id: 1, name: "Jack Sparrow", ship: "Black Pearl", level: 10 },
12    { id: 2, name: "Anne Bonny", ship: "Revenge", level: 8 }
13  ];
14
15  return (
16    <div>
17      <h1>Pirates</h1>
18      {pirates.map((pirate) => (
19        <div key={pirate.id}>
20          <h2>{pirate.name}</h2>
21          <p>Ship: {pirate.ship}</p>
22          <p>Level: {pirate.level}</p>
23        </div>
24      ))}
25    </div>
26  );
27}

Handler de route API

1// app/api/pirates/route.ts
2import { NextRequest, NextResponse } from 'next/server';
3
4interface Pirate {
5  id: number;
6  name: string;
7  level: number;
8}
9
10export async function GET(request: NextRequest) {
11  const pirates: Pirate[] = [
12    { id: 1, name: "Jack Sparrow", level: 10 },
13    { id: 2, name: "Anne Bonny", level: 8 }
14  ];
15
16  return NextResponse.json({ pirates });
17}
18
19export async function POST(request: NextRequest) {
20  const body: Pirate = await request.json();
21
22  // Validation de type
23  if (!body.name || typeof body.level !== 'number') {
24    return NextResponse.json(
25      { error: "Invalid data" },
26      { status: 400 }
27    );
28  }
29
30  return NextResponse.json({ success: true, pirate: body });
31}

Type Metadata

1// app/page.tsx
2import { Metadata } from 'next';
3
4export const metadata: Metadata = {
5  title: "Kraken's Call - Join the Adventure",
6  description: "Epic pirate game built with Next.js",
7  keywords: ["pirate", "game", "adventure"],
8};
9
10export default function HomePage() {
11  return <div>Welcome aboard!</div>;
12}

Type Assertions (as)

Parfois tu en sais plus sur un type que TypeScript :

1interface Pirate {
2  name: string;
3  level: number;
4}
5
6// localStorage renvoie string | null
7const savedData = localStorage.getItem("pirate");
8
9// On dit à TypeScript : « Je sais que c'est une string »
10const pirateData = savedData as string;
11
12// Ou pour du JSON
13const pirate = JSON.parse(pirateData) as Pirate;
14
15// Assertion de type pour un élément du DOM
16const element = document.getElementById("root") as HTMLDivElement;

Attention : utilise as avec prudence - tu peux tromper TypeScript et provoquer une erreur à l'exécution !

Utility Types - Outils intégrés

TypeScript possède des utility types intégrés :

1interface Pirate {
2  name: string;
3  level: number;
4  ship: string;
5  treasure: number;
6}
7
8// Partial - toutes les propriétés optionnelles
9type PartialPirate = Partial<Pirate>;
10// { name?: string; level?: number; ship?: string; treasure?: number; }
11
12// Pick - ne sélectionner que certaines propriétés
13type PiratePreview = Pick<Pirate, "name" | "level">;
14// { name: string; level: number; }
15
16// Omit - exclure certaines propriétés
17type PirateWithoutTreasure = Omit<Pirate, "treasure">;
18// { name: string; level: number; ship: string; }
19
20// Required - toutes les propriétés requises
21type RequiredPirate = Required<PartialPirate>;
22// { name: string; level: number; ship: string; treasure: number; }

any vs unknown vs never

1// any - désactive la vérification de type (à éviter !)
2let anything: any = "hello";
3anything = 42;
4anything.nonExistent();  // Pas d'erreur ! Danger !
5
6// unknown - version plus sûre de any
7let something: unknown = "hello";
8// something.toUpperCase();  // Erreur ! On doit vérifier le type
9
10if (typeof something === "string") {
11  something.toUpperCase();  // OK !
12}
13
14// never - un type qui ne se produit jamais
15function throwError(message: string): never {
16  throw new Error(message);
17  // La fonction ne renvoie jamais de valeur - lance toujours une erreur
18}

tsconfig.json - Configuration TypeScript

Ton projet possède un fichier tsconfig.json avec la configuration TypeScript :

1{
2  "compilerOptions": {
3    "target": "ES2017",                       // Version JS vers laquelle compiler
4    "lib": ["dom", "dom.iterable", "esnext"], // Bibliothèques disponibles
5    "jsx": "react-jsx",                       // Compilation du JSX (réglage de Next.js 16)
6    "module": "esnext",                       // Système de modules
7    "moduleResolution": "bundler",            // Imports résolus comme par un bundler
8    "strict": true,                           // Vérification stricte des types
9    "esModuleInterop": true,                  // Compatibilité avec les modules ES
10    "skipLibCheck": true,                     // Accélère la compilation
11    "noEmit": true,                           // TypeScript vérifie seulement, Next.js compile
12    "paths": { "@/*": ["./*"] }               // L'alias @/ dans les imports
13  }
14}

Options importantes :

  • strict: true - active toutes les vérifications strictes (RECOMMANDÉ !)
  • noImplicitAny: true - n'autorise pas any sans spécification explicite
  • strictNullChecks: true - vérifie si une valeur peut être null/undefined

Utiliser l'IA pour TypeScript

Cursor gère parfaitement TypeScript ! Demande à l'IA :

Prompt 1 :

1Create an interface for a pirate card with name, level, ship and an array of skills

Prompt 2 :

1How to type props for a React component that accepts a title, description and onClick function?

Prompt 3 :

1Fix TypeScript errors in this code: [paste code]

Déboguer les erreurs TypeScript

Erreurs courantes :

1. Property does not exist

1const pirate = { name: "Jack" };
2console.log(pirate.level);  // Property 'level' does not exist
3
4// Correction : ajoute la propriété à l'interface
5interface Pirate {
6  name: string;
7  level?: number;  // Optionnel
8}

2. Type 'X' is not assignable to type 'Y'

1const level: number = "10";  // Erreur : string ≠ number
2
3// Correction : convertis le type
4const fixedLevel: number = parseInt("10");  // OK

3. 'first' is possibly 'undefined' (anciennes versions de TypeScript : Object is possibly 'undefined')

1const pirates = [{ name: "Jack" }];
2const first = pirates.find(p => p.name === "Jack");
3console.log(first.name);  // first pourrait être undefined !
4
5// Correction : optional chaining
6console.log(first?.name);  // OK
7
8// Ou une vérification
9if (first) {
10  console.log(first.name);  // OK
11}

Résumé

TypeScript - JavaScript avec des types, plus de sécurité Types de base - string, number, boolean, array Interface - définir la structure d'un objet Type vs Interface - interface pour les objets, type pour les union types Union types - le type « ou » (string | number) Generics - types universels (<T>) Typage des props - interfaces pour les composants React Utility Types - Partial, Pick, Omit, Required tsconfig.json - configuration TypeScript L'IA aide - Cursor prend en charge TypeScript out-of-the-box

Prochaines étapes : Dans les modules suivants, nous utiliserons TypeScript dans chaque composant. Tu apprendras à typer :

  • Server Components
  • Routes API
  • Requêtes de base de données
  • Formulaires
  • Hooks

À tout de suite !

Code de cette leçon : App.tsx
1// Kraken's Call - cartes de fonctionnalités décrites avec des types TypeScript
2
3interface Feature {
4  title: string;
5  description: string;
6  isNew?: boolean; // champ optionnel
7}
8
9interface FeatureCardProps {
10  feature: Feature;
11}
12
13export default function App() {
14  return (
15    <main style={{ minHeight: "100vh", background: "#0f172a", padding: 32, fontFamily: "system-ui, sans-serif" }}>
16      <h1 style={{ color: "white", marginTop: 0 }}>Kraken's Call</h1>
17      <div style={{ display: "grid", gap: 16 }}>
18        {features.map((feature) => (
19          <FeatureCard key={feature.title} feature={feature} />
20        ))}
21      </div>
22    </main>
23  );
24}
25
26const features: Feature[] = [
27  { title: "Messages rapides", description: "Envoie des messages plus vite qu'un navire toutes voiles dehors", isNew: true },
28  { title: "Cartes au trésor", description: "Partage l'emplacement des trésors avec ton équipage" },
29  { title: "Perroquets IA", description: "Des perroquets intelligents t'aident à communiquer" },
30];
31
32function FeatureCard({ feature }: FeatureCardProps) {
33  return (
34    <div style={{ background: "#1f2937", color: "white", padding: 16, borderRadius: 8 }}>
35      <h3 style={{ margin: "0 0 8px" }}>
36        {feature.title} {feature.isNew && <small style={{ color: "#fbbf24" }}>nouveau</small>}
37      </h3>
38      <p style={{ margin: 0, color: "#9ca3af" }}>{feature.description}</p>
39    </div>
40  );
41}

Tu as repéré une erreur dans cette leçon ?

Teste tes connaissances

Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.

  1. 1. Qu'est-ce que TypeScript ?

  2. 2. À quoi sert une interface en TypeScript ?

Tâches pratiques dans le jeu

  • Tri horizontal

    Ordonne la déclaration d'une variable de type string en TypeScript (de gauche à droite) :

  • Clique dans l'ordre

    Construis un champ 'treasure' optionnel de type number dans une interface :

Articles utiles