Variables et Constantes TypeScript
Conventions de nommage
Variables
- camelCase pour les noms de variables
- Noms descriptifs et significatifs
- Éviter les abréviations ambiguës
// ✅ Bonnes pratiques
const userName = 'john.doe';
const currentTemperature = 25.5;
const isUserAuthenticated = true;
const apiResponse = await fetchUserData();
// ❌ À éviter
const u = 'john.doe'; // Trop court
const temp = 25.5; // Abréviation ambiguë
const auth = true; // Abréviation peu claire
const resp = await fetch(); // Abréviation peu descriptive
Constantes
- SCREAMING_SNAKE_CASE pour les constantes globales
- camelCase pour les constantes locales
- Utiliser
constpar défaut,letuniquement quand la réassignation est nécessaire
// Constantes globales
const MAX_RETRY_ATTEMPTS = 3;
const DEFAULT_TIMEOUT = 5000;
const API_BASE_URL = 'https://api.example.com';
// Constantes locales
const defaultConfig = {
timeout: 5000,
retries: 3
};
const buttonText = 'Save Changes';
Déclaration de Variables
Préférer const à let
- Utiliser
constpar défaut pour éviter les réassignations accidentelles - Utiliser
letuniquement quand la variable doit être réassignée - Ne jamais utiliser
var
// ✅ Préférable
const userConfig = {
theme: 'dark',
language: 'fr'
};
const users: User[] = [];
users.push(newUser); // Mutation de l'objet, pas de réassignation
// ✅ Acceptable quand la réassignation est nécessaire
let currentIndex = 0;
for (const item of items) {
currentIndex++;
processItem(item, currentIndex);
}
// ❌ À éviter
var globalVariable = 'avoid var';
Annotations de type explicites
- Laisser TypeScript inférer les types quand c'est évident
- Utiliser des annotations explicites pour clarifier les types complexes
- Toujours annoter les paramètres de fonction
// ✅ Inférence suffisante
const message = 'Hello World'; // string
const count = 42; // number
const isVisible = true; // boolean
const items = ['a', 'b', 'c']; // string[]
// ✅ Annotations explicites nécessaires
const userProfile: UserProfile = await fetchUserProfile();
const config: DatabaseConfig = {
host: 'localhost',
port: 5432,
database: 'myapp'
};
// ✅ Annotations pour les fonctions
function processData(data: unknown): ProcessedData {
// Implementation
return processedData;
}
const handleClick = (event: MouseEvent): void => {
// Implementation
};
Types primitifs
Types de base
// Types primitifs
const name: string = 'Alice';
const age: number = 30;
const isActive: boolean = true;
const data: unknown = await fetchData();
const id: string | number = getUserId();
// Types spéciaux
const nothing: null = null;
const notDefined: undefined = undefined;
const anything: any = legacyApiCall(); // À éviter autant que possible
Littéraux de type
// Types littéraux pour des valeurs spécifiques
type Theme = 'light' | 'dark' | 'auto';
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
type Status = 'loading' | 'success' | 'error';
const currentTheme: Theme = 'dark';
const requestMethod: HttpMethod = 'POST';
// Constantes avec types littéraux
const SUPPORTED_FORMATS = ['json', 'xml', 'csv'] as const;
type SupportedFormat = typeof SUPPORTED_FORMATS[number]; // 'json' | 'xml' | 'csv'
Types d'objets
Déclaration d'objets typés
// Types d'objets simples
const user: {
id: number;
name: string;
email?: string;
} = {
id: 1,
name: 'John Doe',
email: '[email protected]'
};
// Utilisation d'interfaces (préférable)
interface UserSettings {
theme: Theme;
notifications: boolean;
language: string;
}
const settings: UserSettings = {
theme: 'dark',
notifications: true,
language: 'fr'
};
// Types d'index pour les objets dynamiques
const dictionary: { [key: string]: string } = {
hello: 'bonjour',
goodbye: 'au revoir',
thanks: 'merci'
};
// Utilisation de Record<K, V> (plus expressif)
const statusMessages: Record<Status, string> = {
loading: 'Chargement en cours...',
success: 'Opération réussie',
error: 'Une erreur est survenue'
};
Arrays et Tuples
Arrays typés
// Arrays simples
const numbers: number[] = [1, 2, 3, 4, 5];
const names: string[] = ['Alice', 'Bob', 'Charlie'];
const flags: boolean[] = [true, false, true];
// Arrays d'objets
const users: User[] = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
];
// Arrays en lecture seule
const readonlyNumbers: readonly number[] = [1, 2, 3];
// readonlyNumbers.push(4); // ❌ Erreur de compilation
// Syntax alternative (moins préférable)
const alternativeNumbers: Array<number> = [1, 2, 3];
Tuples
// Tuples pour des structures fixes
type Coordinates = [number, number];
const position: Coordinates = [10, 20];
type ApiResult = [boolean, string, any?];
const result: ApiResult = [true, 'Success', userData];
// Destructuration de tuples
const [x, y] = position;
const [success, message, data] = result;
// Tuples nommés (TypeScript 4.0+)
type NamedCoordinates = [x: number, y: number];
const namedPosition: NamedCoordinates = [10, 20];
Variables d'environnement et configuration
Gestion de la configuration
// Variables d'environnement avec validation
interface EnvironmentConfig {
readonly NODE_ENV: 'development' | 'production' | 'test';
readonly API_URL: string;
readonly DATABASE_URL: string;
readonly PORT: number;
}
const config: EnvironmentConfig = {
NODE_ENV: (process.env.NODE_ENV as EnvironmentConfig['NODE_ENV']) || 'development',
API_URL: process.env.API_URL || 'http://localhost:3000',
DATABASE_URL: process.env.DATABASE_URL || 'postgresql://localhost:5432/myapp',
PORT: parseInt(process.env.PORT || '3000', 10)
};
// Validation des variables d'environnement
function validateConfig(config: EnvironmentConfig): void {
if (!config.API_URL) {
throw new Error('API_URL is required');
}
if (!config.DATABASE_URL) {
throw new Error('DATABASE_URL is required');
}
}
validateConfig(config);
Destructuration
Destructuration d'objets
interface User {
id: number;
name: string;
email: string;
settings: UserSettings;
}
const user: User = fetchUser();
// Destructuration simple
const { id, name, email } = user;
// Destructuration avec renommage
const { name: fullName, email: emailAddress } = user;
// Destructuration avec valeurs par défaut
const { settings = defaultSettings } = user;
// Destructuration imbriquée
const { settings: { theme, notifications } } = user;
// Destructuration avec rest
const { id, ...userWithoutId } = user;
Destructuration d'arrays
const coordinates: [number, number, number] = [10, 20, 30];
// Destructuration simple
const [x, y, z] = coordinates;
// Destructuration partielle
const [firstX, , thirdZ] = coordinates; // Ignore le deuxième élément
// Destructuration avec rest
const [firstCoord, ...restCoords] = coordinates;
// Destructuration dans les paramètres de fonction
function processCoordinates([x, y, z]: [number, number, number]): void {
console.log(`Position: ${x}, ${y}, ${z}`);
}
Gestion des types nullable
Strict null checks
// Avec strictNullChecks activé
let maybeString: string | null = getString();
let maybeNumber: number | undefined = getNumber();
// Vérification avant utilisation
if (maybeString !== null) {
console.log(maybeString.toUpperCase()); // Safe
}
// Utilisation de l'opérateur de coalescence nulle
const displayName = userName ?? 'Guest';
const timeout = userTimeout ?? DEFAULT_TIMEOUT;
// Optional chaining
const userCity = user?.address?.city;
const firstItemName = items?.[0]?.name;
// Non-null assertion (à utiliser avec précaution)
const definiteName = maybeString!; // Assure que maybeString n'est pas null
Type guards pour les validations
function isString(value: unknown): value is string {
return typeof value === 'string';
}
function isUser(obj: unknown): obj is User {
return (
typeof obj === 'object' &&
obj !== null &&
'id' in obj &&
'name' in obj
);
}
// Utilisation des type guards
function processUnknownData(data: unknown): void {
if (isString(data)) {
console.log(data.toUpperCase()); // TypeScript sait que data est string
}
if (isUser(data)) {
console.log(`User: ${data.name}`); // TypeScript sait que data est User
}
}
Bonnes pratiques
1. Initialisation des variables
// ✅ Initialiser lors de la déclaration quand possible
const defaultOptions = {
timeout: 5000,
retries: 3
};
// ✅ Utiliser des valeurs par défaut dans la destructuration
function apiCall({ timeout = 5000, retries = 3 } = {}): Promise<any> {
// Implementation
}
// ✅ Éviter les variables non initialisées
let result: string;
if (condition) {
result = 'success';
} else {
result = 'failure';
}
// Préférer un ternaire ou une fonction
const result = condition ? 'success' : 'failure';
2. Scope des variables
// ✅ Déclarer les variables dans le scope le plus restreint
function processItems(items: Item[]): ProcessedItem[] {
const results: ProcessedItem[] = [];
for (const item of items) {
const processedItem = transformItem(item); // Scope restreint
if (isValid(processedItem)) {
results.push(processedItem);
}
}
return results;
}
3. Éviter la pollution du scope global
// ✅ Utiliser des modules ou namespaces
export const APPLICATION_CONFIG = {
API_TIMEOUT: 5000,
MAX_RETRIES: 3,
DEFAULT_LOCALE: 'fr-FR'
} as const;
// ✅ Encapsuler dans des fonctions ou classes
class ConfigurationManager {
private static instance: ConfigurationManager;
private readonly config: ApplicationConfig;
private constructor() {
this.config = this.loadConfiguration();
}
static getInstance(): ConfigurationManager {
if (!ConfigurationManager.instance) {
ConfigurationManager.instance = new ConfigurationManager();
}
return ConfigurationManager.instance;
}
getConfig(): ApplicationConfig {
return { ...this.config }; // Retourner une copie
}
}