Interfaces et Typage Structurel en TypeScript

Typage structurel et validation des formes

TypeScript repose sur le typage structurel, ce qui signifie que la compatibilité entre deux entités dépend de leurs membres plutôt que de leur nom ou de leur déclaration explicite. Une interface définit le contrat que doit respecter la structure d'un objet.

function validerCommande(details: { reference: string; quantite: number }) {
  console.log(`Commande ${details.reference} confirmée pour ${details.quantite} unité(s).`);
}

const commandeClient = { reference: "CMD-842", quantite: 5, expediteur: "Entrepôt Nord" };
validerCommande(commandeClient); // Valide car la structure minimale est respectée

Éléments facultatifs

Lors de la configuration d'un objet, il est fréquent que certains paramètres soient optionnels. L'opérateur ? indique qu'une propriété peut être absente lors de l'instanciation.

interface ParametresApplication {
  themePrincipal?: string;
  activerNotifications?: boolean;
}

function configurerApp(prefs: ParametresApplication): { theme: string; notifs: boolean } {
  const defauts = { theme: 'clair', notifs: false };
  return {
    theme: prefs.themePrincipal || defauts.theme,
    notifs: prefs.activerNotifications ?? defauts.notifs
  };
}

const config = configurerApp({ themePrincipal: 'sombre' });

Immutabilité des champs

Pour empêcher la modification d'un attribut après sa création initiale, on utilise le mot-clé readonly. Cette notion diffère de const : ce dernier verrouille la liaison d'une varible, tandis que readonly protège les propriétés internes d'un objet ou d'un tableau.

interface Coordonnees {
  readonly latitude: number;
  readonly longitude: number;
}

const position: Coordonnees = { latitude: 48.8566, longitude: 2.3522 };
// position.latitude = 51.5074; // Erreur de compilation : propriété en lecture seule

Validation stricte et signatures d'index

Lorsqu'un objet littéral est passé en paramètre, TypeScript effectue une vérification stricte pour empêcher la présence de clés inattendues. Pour autoriser des propriétés supplémentaires, il faut déclarer une signature d'index explicite.

interface RequeteHTTP {
  url: string;
  methode?: 'GET' | 'POST';
  [cleSupplémentaire: string]: unknown;
}

const req: RequeteHTTP = {
  url: '/api/data',
  methode: 'POST',
  cacheBuster: 123456,
  timeout: 5000
};

Interfaces de fonctions

Une interface peut définir la signature complète d'une fonction, incluant le typage des paramètres d'entrée et le type de retour attendu.

interface Predicate {
  (texte: string, motif: string): boolean;
}

const verifierInclusion: Predicate = (source, recherche) => {
  return source.toLowerCase().includes(recherche.toLowerCase());
};

Types indexables

Cette syntaxe modélise des collections accesibles via un index numérique ou textuel, garantissant le type des valeurs retournées lors de l'accès direct.

interface Historique {
  [indice: number]: string;
}

const logs: Historique = [];
logs.push('Connexion établie');
logs.push('Session active');

const premierMessage: string = logs[0];

Implémentation par des classes

Les interfaces servent de plan contractuel pour les classes via le mot-clé implements. Notez que le compilateur ne vérifie pas les constructeurs, uniquement les propriétés et méthodes publiques définies dans le contrat.

interface Peripherique {
  statut: 'actif' | 'inactif';
  allumer(): void;
}

class Imprimante implements Peripherique {
  statut: 'actif' | 'inactif' = 'inactif';

  allumer(): void {
    this.statut = 'actif';
    console.log('Périphérique prêt.');
  }
}

Héritage entre interfaces

Le mécanisme d'héritage permet de composer des interfaces en étendant des définitions existantes, favorisant la réutilisation sans duplication de code.

interface ComposantVisuel {
  identifiant: string;
}

interface ElementNavigable extends ComposantVisuel {
  lienUrl: string;
  cliquer(): void;
}

const menu: ElementNavigable = {
  identifiant: 'nav-01',
  lienUrl: '/accueil',
  cliquer: () => console.log('Redirection...')
};

Types hybrides et combinés

Il est possible de fusionner des signatures de fonctions, des propriétés et des méthodes au sein d'une même interface. Ce modèle est particulièrement utile pour créer des objets appelables qui conservent un état interne.

interface OutilAnalyse {
  (donnees: number[]): number;
  precision: number;
  recalibrer(): void;
}

function genererOutil(): OutilAnalyse {
  const outil = ((valeurs: number[]) => {
    return valeurs.reduce((somme, val) => somme + val, 0) / valeurs.length;
  }) as OutilAnalyse;

  outil.precision = 0.95;
  outil.recalibrer = function() { outil.precision = 1.0; };

  return outil;
}

const calculateur = genererOutil();
const moyenne = calculateur([10, 20, 30]);
calculateur.recalibrer();

Étiquettes: TypeScript interfaces typage-structurel signatures-de-fonctions Immutabilité

Publié le 2 septembre à 15h02