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

Typing consistency is essential to ensure robust and reliable code. StartER uses TypeScript to bring this consistency across all code, whether on the client (React) or server (Express) side. This page introduces two fundamental typing techniques in our framework.

Common declarations in src/types/index.d.ts

The src/types/index.d.ts file is the central point for defining types shared across 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;
};

The types declared in this file are available throughout the application without requiring any imports.

In React components, these types are used directly. For example, in src/react/components/ItemContext.tsx:

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

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

async readAll() {
  // ...

  return rows as Item[];
}

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

Declaration merging for Express's 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 is called declaration merging. In StartER, this technique is used to enrich Express's Request interface with module-specific properties.

Each Express module that needs to add a custom property to the Request object declares this extension via declare global. 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

This way, each module only declares the extensions it needs. The TypeScript IDE and compiler recognize the added properties as legitimate.

Best practices

  1. Keep core types in index.d.ts: Types used by both Express and React should be defined in this file.

  2. One declaration per property: Each extension to the Request interface should be made in the module that applies to it.

  3. Consistent naming: Properties added to Request should follow a consistent naming convention.

  4. Avoid collisions: Ensure different modules don't use the same property name.

  5. Document the intent: Add comments explaining why a property is added to Request.


The combination of these two techniques (centralizing common types and extending existing interfaces via declaration merging) allows us to maintain a robust and consistent typing system throughout the framework.

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