Types en US - rocambille/start-express-react GitHub Wiki

Typing consistency is essential to ensure code robustness and reliability. StartER relies on TypeScript to ensure this consistency end-to-end: from client (React) to server (Express). This page introduces two fundamental typing techniques used in the framework.

Shared types: src/types/index.d.ts

The src/types/index.d.ts file is the central point for defining types shared between all parts of the application. It provides a set of types that are accessible globally without the need for explicit imports.

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;
};

Types defined here are automatically available throughout the code, without import.

In React components, these types are used directly. For example, in src/react/components/item/useItems.ts:

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

Here, Item[] indicates that the items variable contains an array of objects conforming to the Item type.

Similarly, in Express modules, these types are used to ensure consistency. For example, in src/express/modules/item/itemRepository.ts:

async readAll() {
  // ...

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

The same Item type is used in both Express and React, ensuring perfect consistency between the backend and frontend.

Declaration merging for Express Request interface

Interfaces in TypeScript are open. If an interface is defined with the name of an existing interface, its properties are added to the original. This capability, called declaration merging, allows properties to be added to an existing interface without redefining it entirely. StartER leverages this technique to enrich Express Request interface with module-specific data.

Every Express module that adds a property to the Request object does so via a declare global block. For example, in src/express/modules/item/itemParamConverter.ts:

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

After this declaration, TypeScript recognizes req.item as a valid property of type Item on all Express Request objects in the application.

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

Thus, each module extends Request with what it needs, with a cascading effect on the rest of the project. The TypeScript IDE and compiler recognize the added properties as legitimate.

Best practices

  1. Keep common types in index.d.ts: These are the ones shared between Express and React.

  2. One declaration = one responsibility: each module must extend Request only for its needs.

  3. Name properties consistently (like req.item, req.user...).

  4. Avoid name collisions between modules.

  5. Document why a property is added.


By combining common type centralization and declaration merging, StartER maintains strong, consistent, and scalable typing across all code.

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