AgentSkillsCN

typescript-standards

项目中的TypeScript约定。适用于“add types”、“typescript”、“interface”、“type definition”等场景。

SKILL.md
--- frontmatter
name: typescript-standards
description: Conventions TypeScript pour le projet. Use when "add types", "typescript", "interface", "type definition".
allowed-tools: Read, Grep, Glob

TypeScript Standards

Purpose

Définir les conventions TypeScript pour le monorepo consultant-manager.

Principes Généraux

Strictness

  • Activer strict: true dans tsconfig.json
  • Pas de any sauf justification documentée
  • Préférer unknown à any si type inconnu
  • Utiliser as const pour les littéraux immuables

Organisation des Types

Types Partagés (utilisés par backend ET frontend):

typescript
// shared/types.ts
export interface Consultant {
  id: string;
  nom: string;
  prenom: string;
  // ...
}

Types Backend Spécifiques:

typescript
// backend/src/types/index.ts
import { Consultant } from '@shared/types';
import { Prisma } from '@prisma/client';

Types Frontend Spécifiques:

typescript
// frontend/src/types/index.ts
import { Consultant } from '@shared/types';

export interface ConsultantFormData extends Omit<Consultant, 'id' | 'dateCreation'> {
  // ...
}

Conventions de Nommage

Interfaces vs Types

typescript
// ✅ Interface pour objets et classes
interface Consultant {
  id: string;
  nom: string;
}

// ✅ Type pour unions, intersections, utilitaires
type ConsultantStatus = 'DISPONIBLE' | 'EN_MISSION' | 'EN_CONGES' | 'INDISPONIBLE';
type PartialConsultant = Partial<Consultant>;

Nommage

  • Interfaces/Types: PascalCase (Consultant, MissionWithConsultant)
  • Enums: PascalCase (StatutConsultant)
  • Type guards: is prefix (isConsultant, hasActiveMission)
  • Génériques: Lettres simples ou descriptif (T, TData, TResponse)

Patterns Spécifiques au Projet

Backend: Request/Response Types

typescript
// Controllers avec types explicites
import { Request, Response } from 'express';

export const createConsultant = async (req: Request, res: Response) => {
  const validatedData = consultantSchema.parse(req.body);
  // ...
};

// Typer les paramètres de route
interface ConsultantParams {
  id: string;
}

export const getConsultant = async (
  req: Request<ConsultantParams>,
  res: Response
) => {
  const { id } = req.params;
  // ...
};

Frontend: Props et States

typescript
// Props d'un composant
interface ConsultantFormProps {
  consultant: Consultant | null;
  onClose: () => void;
  onSave?: (consultant: Consultant) => void;
}

export default function ConsultantForm({
  consultant,
  onClose,
  onSave
}: ConsultantFormProps) {
  // ...
}

// State hooks typés
const [consultants, setConsultants] = useState<Consultant[]>([]);
const [loading, setLoading] = useState<boolean>(false);
const [error, setError] = useState<string | null>(null);

Prisma Types

typescript
import { Consultant, Mission, Prisma } from '@prisma/client';

// Type généré incluant relations
type ConsultantWithMissions = Prisma.ConsultantGetPayload<{
  include: { missions: true }
}>;

// Utiliser dans les fonctions
function calculateStatus(consultant: ConsultantWithMissions): string {
  // ...
}

Utilitaires TypeScript

Types Utilitaires Courants

typescript
// Omit pour exclure des champs
type ConsultantCreate = Omit<Consultant, 'id' | 'dateCreation' | 'dateModification'>;

// Pick pour sélectionner des champs
type ConsultantSummary = Pick<Consultant, 'id' | 'nom' | 'prenom' | 'statut'>;

// Partial pour rendre tout optionnel
type ConsultantUpdate = Partial<ConsultantCreate>;

// Required pour rendre tout requis
type ConsultantFull = Required<Consultant>;

Type Guards

typescript
// Vérifier qu'un objet est un Consultant
function isConsultant(obj: unknown): obj is Consultant {
  return (
    typeof obj === 'object' &&
    obj !== null &&
    'id' in obj &&
    'nom' in obj &&
    'prenom' in obj
  );
}

// Utilisation
if (isConsultant(data)) {
  console.log(data.nom); // TypeScript sait que data est Consultant
}

Zod et TypeScript

Inférer Types depuis Zod

typescript
import { z } from 'zod';

const consultantSchema = z.object({
  nom: z.string().min(1),
  prenom: z.string().min(1),
  email: z.string().email(),
  tjm: z.number().positive()
});

// Inférer le type depuis le schema
type ConsultantInput = z.infer<typeof consultantSchema>;

// Utiliser dans la fonction
function validateConsultant(data: unknown): ConsultantInput {
  return consultantSchema.parse(data);
}

Date Handling

typescript
// Toujours typer les dates clairement
interface Mission {
  dateDebut: Date;      // Objet Date
  dateFin: Date;        // Objet Date
}

// Pour les API responses (JSON)
interface MissionResponse {
  dateDebut: string;    // ISO 8601 string
  dateFin: string;      // ISO 8601 string
}

// Conversion
function toMissionResponse(mission: Mission): MissionResponse {
  return {
    ...mission,
    dateDebut: mission.dateDebut.toISOString(),
    dateFin: mission.dateFin.toISOString()
  };
}

Erreurs Courantes à Éviter

❌ Mauvais: any partout

typescript
function processData(data: any): any {
  return data.map((item: any) => item.value);
}

✅ Bon: Types explicites

typescript
function processData(data: Consultant[]): number[] {
  return data.map(consultant => consultant.tjm);
}

❌ Mauvais: Types incomplets

typescript
interface Consultant {
  nom: string;
  // Oubli d'autres champs...
}

✅ Bon: Types complets alignés avec Prisma

typescript
interface Consultant {
  id: string;
  nom: string;
  prenom: string;
  email: string;
  telephone: string | null;
  competences: string[];
  tjm: number;
  statut: ConsultantStatus;
  dateCreation: Date;
  dateModification: Date;
}

❌ Mauvais: Assertions non sûres

typescript
const consultant = data as Consultant; // Dangereux!

✅ Bon: Validation avec Zod

typescript
const consultant = consultantSchema.parse(data); // Sûr, validé

Checklist TypeScript

Avant de commiter du code TypeScript:

  • Aucune utilisation de any (ou justifiée et documentée)
  • Types partagés dans /shared/types si utilisés par plusieurs workspaces
  • Interfaces pour tous les objets métier (Consultant, Mission, etc.)
  • Props React typées
  • Request/Response typés côté backend
  • Validation Zod avec inférence de types
  • Pas d'assertions as dangereuses
  • Dates typées correctement (Date vs string)
  • npm run build passe sans erreurs TypeScript

Ressources