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();