Types - rocambille/start-express-react GitHub Wiki

La cohérence du typage est essentielle pour assurer la robustesse et la fiabilité du code. StartER s'appuie sur TypeScript pour garantir cette cohérence de bout en bout : du client (React) au serveur (Express). Cette page présente deux techniques fondamentales de typage utilisées dans le framework.

Types partagés : src/types/index.d.ts

Le fichier src/types/index.d.ts constitue le point central de définition des types partagés entre toutes les parties de l'application. Il fournit un ensemble de types qui sont accessibles globalement sans avoir besoin d'imports explicites.

type Credentials = {
  email: string;
  password: string;
};

type Item = {
  id: number;
  title: string;
  user_id: number;
};

type User = {
  id: number;
  email: string;
};

type UserWithPassword = User & {
  password: string;
};

Les types définis ici sont automatiquement disponibles dans tout le code, sans import.

Dans les composants React, ces types sont utilisés directement. Par exemple, dans src/react/components/item/hooks.ts :

const items = use<Item[]>(cache("/api/items"));

Ici, Item[] indique que la variable items contient un tableau d'objets conformes au type Item.

De même, dans les modules Express, ces types sont utilisés pour garantir la cohérence. Par exemple, dans src/express/modules/item/itemRepository.ts :

async readAll() {
  // ...

  return rows.map<Item>(({ id, title, user_id }) => ({ id, title, user_id }));
}

Le même type Item est utilisé à la fois dans Express et React, garantissant une cohérence parfaite entre le backend et le frontend.

Fusion de déclaration pour l'interface Request d'Express

Les interfaces dans TypeScript sont ouvertes. Si une interface est définie avec le nom d'une interface existante, ses propriétés s'ajoutent à l'original. Cette capacité, appelée declaration merging, permet d'ajouter des propriétés à une interface existante sans la redéfinir entièrement. StartER exploite cette technique pour enrichir l'interface Request d'Express avec des données spécifiques à chaque module.

Chaque module Express qui ajoute une propriété à l'objet Request le fait via un bloc declare global. Par exemple, dans src/express/modules/item/itemParamConverter.ts :

declare global {
  namespace Express {
    interface Request {
      item: Item;
    }
  }
}

Après cette déclaration, TypeScript reconnaît req.item comme une propriété valide de type Item sur tous les objets Request d'Express dans l'application.

req.item = item;  // OK: req.item exists

Ainsi, chaque module étend Request avec ce dont il a besoin, avec un effet de cascade sur le reste du projet. L'IDE et le compilateur TypeScript reconnaissent les propriétés ajoutées comme légitimes.

Bonnes pratiques

  1. Gardez les types communs dans index.d.ts : ce sont ceux partagés entre Express et React.

  2. Une déclaration = une responsabilité : chaque module doit étendre Request uniquement pour ses besoins.

  3. Nommez les propriétés de manière cohérente (par exemple req.item, req.user...).

  4. Évitez les collisions de noms entre modules.

  5. Documentez pourquoi une propriété est ajoutée.


En combinant la centralisation des types communs et la fusion de déclarations, StartER maintient un typage fort, cohérent et évolutif sur l'ensemble du code.

⚠️ **GitHub.com Fallback** ⚠️