Aller au contenu

Conventions de codage TypeScript / React


Portee

Ce document definit les conventions de codage TypeScript et React applicables a tous les projets frontend de l'organisation EIP-MemTide, notamment FrontWeb. Il couvre le nommage, l'architecture, le typage, les patterns React, le formatage et les tests.

Toute contribution (PR, commit, revue) doit etre conforme a ces regles. En cas de doute, ce document fait reference.

Relation avec les regles machine : le fichier Claude/rules/Typescript/conventions-typescript.md contient les memes regles sous forme d'instructions strictes pour les agents IA. Les deux documents doivent rester synchronises.


1. Langue

Tout le code, les commentaires, les noms de variables, les noms de fonctions et la documentation doivent etre rediges en anglais americain (U.S. English). Les commentaires ou identifiants en francais sont interdits.


2. Conventions de nommage

Element Convention Exemple
Fichiers composants PascalCase.tsx MemoryDashboard.tsx
Fichiers utilitaires camelCase.ts formatUptime.ts
Composants React PascalCase HomePage, MemoryBlock
Fonctions et variables camelCase formatUptime(), totalBytes
Constantes SCREAMING_SNAKE_CASE API_TIMEOUT, MAX_RETRIES
Interfaces et types PascalCase MemoryBlock, TopologyData
Enums et valeurs d'enum PascalCase TierType, TierType.NumaDram
Repertoires camelCase features/, shared/

Regles complementaires :

  • Fonctions : verbes decrivant l'action (fetchSnapshot, calculateOffset). Eviter process, handle.
  • Variables et types : noms explicites (memoryBlocks, numaNodeCount).
  • Booleens : prefixes is, has, can, should (isLoading, hasError).
// Mauvais
const data = getStuff();
const flag = true;

// Bon
const numaNodes = fetchTopology();
const isHardwareReady = true;

3. Architecture — Feature-Sliced Design

3.1 Couches et sens des imports

app  -->  features  -->  entities  -->  shared
Couche Responsabilite Peut importer depuis Interdit
app/ Point d'entree, routing, providers, styles globaux features, entities, shared
features/ Modules fonctionnels autonomes (logique metier + UI) entities, shared Autres features, app
entities/ Types du domaine, helpers purs shared features, app
shared/ Utilitaires generiques, composants UI de base, theme Rien (sauf npm) Toutes les couches

Regle d'or : aucun import lateral entre features. Si deux features partagent de la logique, l'extraire dans entities/ ou shared/.

3.2 Structure d'une feature

Chaque feature est organisee en sous-repertoires api/ (clients HTTP, DTOs, mappers), model/ (stores, hooks, machines a etats) et ui/ (composants React, CSS).

3.3 Barrel exports

Chaque module expose un index.ts comme point d'entree public :

// entities/memory/index.ts
export type { MemoryBlock, MemorySnapshot } from './types';

// Import depuis le barrel (correct)
import { MemoryBlock } from 'entities/memory';
// Import direct (a eviter)
import { MemoryBlock } from 'entities/memory/types';

3.4 Structure type du projet

src/ contient quatre repertoires racines alignes sur les couches FSD : app/ (App.tsx, styles globaux), entities/ (un sous-repertoire par domaine avec types.ts + index.ts), features/ (un sous-repertoire par feature avec api/, model/, ui/), shared/ (assets/, lib/, theme/, ui/).


4. Regles TypeScript

4.1 Mode strict

tsconfig.json doit activer "strict": true (strictNullChecks, noImplicitAny, strictFunctionTypes, etc.).

4.2 Interdiction de any

Utiliser unknown + type guard quand le type est inconnu :

function parse(data: unknown): string {
  if (typeof data !== 'string') throw new Error('Expected string');
  return data;
}

4.3 Types de retour explicites

Obligatoire sur toutes les fonctions exportees : export function formatGiB(bytes: number): string { ... }

4.4 Interface vs Type

Utiliser Quand
interface Formes d'objets, props de composants, contrats API
type Unions, intersections, types conditionnels, types mappes

4.5 Unions discriminees

Privilegier les unions discriminees pour les etats complexes. Elles eliminent les combinaisons d'etats impossibles :

// MAUVAIS — etats impossibles representables
interface State { isLoading: boolean; isError: boolean; data?: MemorySnapshot; }

// BON — chaque etat est explicite et exhaustif
type State =
  | { status: 'idle' }
  | { status: 'loading' }
  | { status: 'success'; data: MemorySnapshot }
  | { status: 'error'; error: string };

4.6 Assertions de type

  • as const pour les objets litteraux non mutables.
  • Eviter as Type sauf necessite absolue. Jamais de as any.
  • @ts-ignore interdit ; @ts-expect-error tolere uniquement avec commentaire justificatif.

5. Regles React

5.1 Composants fonctionnels uniquement

Les class components sont interdits. Utiliser exclusivement des fonctions avec hooks :

export function MemoryGrid({ blocks, tierType }: MemoryGridProps): React.ReactElement {
  return ( ... );
}

5.2 Un composant par fichier, named export

  • Chaque .tsx contient un seul composant exporte (sous-composants locaux non exportes toleres si < 20 lignes).
  • Utiliser des named exports, jamais export default pour les composants.

5.3 Props explicites

Les interfaces de props sont nommees <ComponentName>Props et les props sont destructurees dans la signature :

interface PidFormProps {
  onSubmit: (pid: number) => void;
  isLoading: boolean;
}

export function PidForm({ onSubmit, isLoading }: PidFormProps): React.ReactElement { ... }

5.4 Structure d'un fichier composant

Ordre recommande :

  1. Imports (externes, puis internes par couche FSD descendante)
  2. Interface des props
  3. Constantes locales
  4. Le composant (fonction exportee)
import { useState } from 'react';
import { MemoryBlock } from 'entities/memory';
import { Card } from 'shared/ui/Card';
import './dashboard.css';

interface MemoryGridProps { blocks: MemoryBlock[]; tierType: 'dram' | 'cxl'; }
const BLOCK_SIZE_PX = 24;

export function MemoryGrid({ blocks, tierType }: MemoryGridProps): React.ReactElement {
  const [selected, setSelected] = useState<number | null>(null);
  return ( ... );
}

5.5 Hooks

  • Appels uniquement au niveau superieur : jamais dans des conditions ou boucles.
  • Hooks personnalises prefixes par use : useMemoryDashboard, useTopologyStore.
  • Logique metier dans model/ (hooks), affichage dans ui/ (composants).

5.6 Error Boundaries et Suspense

Envelopper les sous-arbres critiques pour la resilience :

<ErrorBoundary fallback={<ErrorBanner message="Something went wrong" />}>
  <Suspense fallback={<Loader />}>
    <MemoryDashboardPage />
  </Suspense>
</ErrorBoundary>

6. Patterns interdits et requis

6.1 Patterns interdits

Pattern interdit Alternative
any Typage precis ou unknown + type guard
Class components Composants fonctionnels + hooks
var const par defaut, let si reassignation
Inline styles (style={{ ... }}) Fichiers CSS, variables CSS
console.log en production Logger structure ou suppression
default export pour composants Named export
@ts-ignore Corriger le type ou @ts-expect-error avec commentaire
Import lateral entre features Extraire vers entities/ ou shared/
Commentaires en francais Anglais americain uniquement

6.2 Patterns requis

Pattern requis Raison
"strict": true dans tsconfig Securite de type maximale
Types de retour explicites sur les exports Auto-documentation, refactoring fiable
Unions discriminees pour les etats Elimination des etats impossibles
Props destructurees dans la signature Lisibilite
Error Boundaries sur les sous-arbres critiques Resilience
Suspense pour le lazy loading UX fluide
Barrel exports (index.ts) par module API publique claire
Hooks personnalises pour la logique metier Separation modele / vue

7. Formatage et outillage

7.1 Configuration

Outil Role Configuration
Prettier Formatage automatique 2 espaces, 100 colonnes, single quotes, trailing commas, semicolons, LF
ESLint Analyse statique Preset react-app + regles strictes TypeScript
TypeScript Verification de types strict: true, noUncheckedIndexedAccess: true

7.2 Regles de formatage

  • Indentation : 2 espaces (jamais de tabulations).
  • Limite de colonnes : 100 caracteres.
  • Guillemets : single quotes (') pour les chaines, double quotes (") dans le JSX.
  • Points-virgules : obligatoires.
  • Virgules finales : obligatoires (trailing commas).
  • Fin de ligne : LF (Unix).

7.3 Commandes

Action Commande
Formater le code npm run format
Verifier le formatage npm run format:check
Linter npm run lint
Verifier les types npm run typecheck

8. Styles CSS

Le projet utilise des fichiers CSS purs organises par couche FSD avec des variables CSS comme source de verite.

Fichier Portee Contenu
shared/theme/variables.css Global (:root) Tokens : couleurs, espacements, polices
shared/ui/styles.css Composants partages .shared-button, .shared-card
features/*/ui/*.css Par feature Styles specifiques
app/styles/global.css Layout global Sidebar, header, contenu

Regles : inline styles interdits, noms de classes en .feature-component ou .shared-component, tokens references via var(--token-name).


9. Tests

Outil Role
Vitest ou Jest Tests unitaires
React Testing Library Tests de composants
MSW Mocking des appels API
  • Fichiers co-localises : MyComponent.test.tsx a cote de MyComponent.tsx.
  • Noms descriptifs avec describe / it.
  • Tester le comportement visible, pas les details d'implementation.
  • Utiliser screen.getByRole, screen.getByText, screen.getByLabelText.
describe('MemoryGrid', () => {
  it('renders the correct number of blocks', () => {
    render(<MemoryGrid blocks={mockBlocks} tierType="dram" />);
    expect(screen.getAllByRole('cell')).toHaveLength(mockBlocks.length);
  });
});

Chaque PR doit inclure des tests pour le code modifie. Aucune regression de couverture acceptee.


10. Commentaires

  • En anglais, concis. Expliquer le pourquoi, pas le comment.
  • Pas d'emojis, d'ASCII art ou de decorations.
  • JSDoc encourage sur les fonctions et composants publics :
/** Converts a raw byte count to a human-readable GiB string. */
export function formatGiB(bytes: number): string {
  return `${(bytes / (1024 ** 3)).toFixed(2)} GiB`;
}

11. Checklist avant commit

  1. npm run format — formater le code.
  2. npm run lint — corriger les erreurs de lint.
  3. npm run typecheck — zero erreur TypeScript.
  4. npm test — tous les tests passent.
  5. Verifier le sens des imports FSD (pas d'import lateral entre features).
  6. Verifier l'absence de any, console.log et default export de composant.