Skip to main content

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 const par défaut, let uniquement 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 const par défaut pour éviter les réassignations accidentelles
  • Utiliser let uniquement 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
}
}