Retour au blog

TypeScript en 2025 : Pourquoi 38.5% des Développeurs l'Ont Choisi et Vous Devriez Aussi

Salut HaWkers, TypeScript affiche 38.5% de popularité selon les enquêtes de 2025, se consolidant comme l'un des top 5 langages de programmation au monde. Mais est-ce juste du battage médiatique ou y a-t-il des raisons solides de l'adopter ?

Dans cet article, nous allons explorer pourquoi TypeScript a dominé l'écosystème JavaScript, comment démarrer de manière pratique et les stratégies de migration pour vos projets existants.

Pourquoi TypeScript a Gagné la Bataille

1. La Sécurité des Types Prévient les Bugs Coûteux

// ❌ JavaScript : Le bug n'apparaît qu'en production
function calculateDiscount(price, percentage) {
  return price - (price * percentage / 100);
}

// Bug : quelqu'un passe une chaîne par erreur
calculateDiscount('100', 20); // '100-NaN' = NaN 😱

// ✅ TypeScript : Erreur détectée en temps de développement
function calculateDiscount(price: number, percentage: number): number {
  return price - (price * percentage / 100);
}

// Erreur en temps de compilation !
calculateDiscount('100', 20);
// Error: Argument of type 'string' is not assignable to parameter of type 'number'

2. IntelliSense et Autocomplétion Puissants

// TypeScript fournit une autocomplétion précise
interface User {
  id: string;
  name: string;
  email: string;
  role: 'admin' | 'user' | 'guest';
  createdAt: Date;
  metadata?: {
    lastLogin?: Date;
    preferences: {
      theme: 'light' | 'dark';
      notifications: boolean;
    };
  };
}

function greetUser(user: User) {
  // L'IDE suggère toutes les propriétés disponibles
  console.log(`Hello, ${user.name}!`);

  // Autocomplétion dans les objets imbriqués
  if (user.metadata?.preferences.theme === 'dark') {
    // L'IDE sait exactement ce qui est disponible
    console.log('Dark mode enabled');
  }

  // Les types littéraux préviennent les fautes de frappe
  if (user.role === 'admim') { // Erreur : 'admim' n'existe pas
    // ...
  }
}

3. Refactoring Sécurisé dans les Grandes Bases de Code

// Scénario : Renommer une propriété dans 100 fichiers

// Interface originale
interface Product {
  productName: string;
  price: number;
}

// Refactoriser vers 'name' au lieu de 'productName'
interface Product {
  name: string; // TypeScript montre TOUS les endroits cassés
  price: number;
}

// Tous les usages sont automatiquement identifiés
function displayProduct(product: Product) {
  return `${product.productName} - $${product.price}`;
  // Erreur : Property 'productName' does not exist on type 'Product'
  // Suggestion : Did you mean 'name'?
}

// En JavaScript : Vous découvririez ce bug en production 💥

TypeScript en Pratique : Du Basique à l'Avancé

Types Primitifs et Basiques

// Types basiques
let isDone: boolean = false;
let count: number = 42;
let name: string = 'John';
let notSure: any = 4; // Évitez d'utiliser 'any' !

// Tableaux
let numbers: number[] = [1, 2, 3];
let strings: Array<string> = ['a', 'b', 'c'];

// Tuples (tableaux avec types fixes)
let tuple: [string, number] = ['age', 30];

// Enum
enum Status {
  Pending = 'PENDING',
  Approved = 'APPROVED',
  Rejected = 'REJECTED'
}

const orderStatus: Status = Status.Pending;

// Union Types (types alternatifs)
let id: string | number;
id = '123'; // OK
id = 123;   // OK
id = true;  // Erreur

// Literal Types (valeurs spécifiques)
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';

function makeRequest(url: string, method: HttpMethod) {
  // method ne peut être qu'une des valeurs spécifiées
}

makeRequest('/api/users', 'GET');     // OK
makeRequest('/api/users', 'PATCH');   // Erreur

Interfaces et Type Aliases

// Interface : Définit la structure des objets
interface User {
  id: string;
  name: string;
  email: string;
  age?: number; // Optionnel
  readonly createdAt: Date; // Lecture seule
}

// Extension d'interfaces
interface AdminUser extends User {
  permissions: string[];
  role: 'admin';
}

// Type Alias : Similaire à interface, mais plus flexible
type Point = {
  x: number;
  y: number;
};

// Type pour union types
type ID = string | number;

// Type pour fonctions
type GreetFunction = (name: string) => string;

const greet: GreetFunction = (name) => `Hello, ${name}!`;

// Interface vs Type : Quand utiliser ?
// Utilisez Interface pour les objets qui peuvent être étendus
// Utilisez Type pour les unions, primitifs, tuples

// Intersection Types (combiner des types)
type WithTimestamp = {
  createdAt: Date;
  updatedAt: Date;
};

type UserWithTimestamp = User & WithTimestamp;

const user: UserWithTimestamp = {
  id: '1',
  name: 'John',
  email: 'john@example.com',
  createdAt: new Date(),
  updatedAt: new Date()
};

Generics : Types Réutilisables

// Generic simple
function identity<T>(arg: T): T {
  return arg;
}

const num = identity<number>(42);
const str = identity<string>('hello');

// Generic avec contraintes
interface HasLength {
  length: number;
}

function logLength<T extends HasLength>(arg: T): T {
  console.log(arg.length); // OK, T a length
  return arg;
}

logLength('hello');     // OK, string a length
logLength([1, 2, 3]);   // OK, array a length
logLength(42);          // Erreur, number n'a pas length

// Generic dans les interfaces
interface Response<T> {
  data: T;
  status: number;
  message: string;
}

interface User {
  id: string;
  name: string;
}

const userResponse: Response<User> = {
  data: { id: '1', name: 'John' },
  status: 200,
  message: 'Success'
};

const usersResponse: Response<User[]> = {
  data: [
    { id: '1', name: 'John' },
    { id: '2', name: 'Jane' }
  ],
  status: 200,
  message: 'Success'
};

// Generic utility types
type Partial<T> = { [P in keyof T]?: T[P] };
type Required<T> = { [P in keyof T]-?: T[P] };
type Readonly<T> = { readonly [P in keyof T]: T[P] };
type Pick<T, K extends keyof T> = { [P in K]: T[P] };

// Usage pratique
interface TodoItem {
  id: string;
  title: string;
  completed: boolean;
  createdAt: Date;
}

// Partial : Tous les champs optionnels
type TodoUpdate = Partial<TodoItem>;

function updateTodo(id: string, updates: TodoUpdate) {
  // Peut passer uniquement les champs à mettre à jour
}

updateTodo('1', { completed: true }); // OK
updateTodo('2', { title: 'New title', completed: false }); // OK

// Pick : Sélectionner uniquement des champs spécifiques
type TodoSummary = Pick<TodoItem, 'id' | 'title'>;

const summary: TodoSummary = {
  id: '1',
  title: 'Task'
  // completed et createdAt ne sont pas nécessaires
};

Cas d'Usage Avancés

// 1. Discriminated Unions (Pattern Matching)
type Success<T> = {
  type: 'success';
  data: T;
};

type Error = {
  type: 'error';
  error: string;
};

type Result<T> = Success<T> | Error;

function handleResult<T>(result: Result<T>) {
  // TypeScript sait exactement quel type basé sur 'type'
  if (result.type === 'success') {
    console.log(result.data); // OK, TypeScript sait qu'il a 'data'
  } else {
    console.log(result.error); // OK, TypeScript sait qu'il a 'error'
  }
}

// 2. Conditional Types
type IsString<T> = T extends string ? 'yes' : 'no';

type A = IsString<string>;  // 'yes'
type B = IsString<number>;  // 'no'

// 3. Mapped Types
type Optional<T> = {
  [K in keyof T]?: T[K];
};

type RequiredUser = {
  name: string;
  email: string;
  age: number;
};

type OptionalUser = Optional<RequiredUser>;
// { name?: string; email?: string; age?: number; }

// 4. Template Literal Types (TypeScript 4.1+)
type HTTPMethod = 'GET' | 'POST' | 'PUT';
type Route = '/users' | '/products' | '/orders';

type APIEndpoint = `${HTTPMethod} ${Route}`;
// 'GET /users' | 'GET /products' | 'GET /orders' |
// 'POST /users' | 'POST /products' | 'POST /orders' |
// 'PUT /users' | 'PUT /products' | 'PUT /orders'

// 5. Type Guards
function isString(value: unknown): value is string {
  return typeof value === 'string';
}

function processValue(value: unknown) {
  if (isString(value)) {
    console.log(value.toUpperCase()); // OK, TypeScript sait que c'est string
  }
}

// 6. Decorators (Expérimental)
function logged(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;

  descriptor.value = function (...args: any[]) {
    console.log(`Calling ${propertyKey} with`, args);
    const result = originalMethod.apply(this, args);
    console.log(`Result:`, result);
    return result;
  };

  return descriptor;
}

class Calculator {
  @logged
  add(a: number, b: number): number {
    return a + b;
  }
}

Migration Graduelle de JavaScript vers TypeScript

Stratégie de Migration

// 1. Configuration initiale (tsconfig.json)
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "lib": ["ES2020", "DOM"],
    "jsx": "react-jsx",
    "strict": false, // Commencer avec false, activer progressivement
    "allowJs": true, // Permettre les fichiers .js pendant la migration
    "checkJs": false, // Ne pas vérifier les fichiers .js initialement
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true
  },
  "include": ["src"],
  "exclude": ["node_modules"]
}

// 2. Plan de migration
const migrationPlan = {
  'Phase 1 - Setup': [
    'Installer TypeScript et types',
    'Configurer tsconfig.json',
    'Renommer 1 fichier .js → .ts pour tester',
    'Configurer build pipeline'
  ],
  'Phase 2 - Nouveaux Fichiers': [
    'Tous nouveaux fichiers en TypeScript',
    'Créer types.d.ts pour interfaces communes'
  ],
  'Phase 3 - Migration Graduelle': [
    'Migrer modules utilitaires en premier',
    'Migrer composants indépendants',
    'Migrer composants avec dépendances',
    'Activer strict mode progressivement'
  ],
  'Phase 4 - Strictness': [
    'Activer noImplicitAny',
    'Activer strictNullChecks',
    'Activer strictFunctionTypes',
    'Activer strictPropertyInitialization'
  ]
};

// 3. Types pour bibliothèques externes
// Installer @types packages
npm install --save-dev @types/react @types/node @types/express

// 4. Créer fichiers de déclaration pour code legacy
// legacy.d.ts
declare module 'old-library' {
  export function doSomething(param: string): void;
}

// 5. Utility types pour migration
// any-to-unknown.ts
type TODO = any; // Marqueur temporaire, à remplacer plus tard
type FIXME = any; // Marque bugs connus

// 6. Exemple de migration progressive
// Avant (JavaScript)
function calculateTotal(items) {
  return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}

// Étape 1 : Ajouter JSDoc (préparation)
/**
 * @param {Array<{price: number, quantity: number}>} items
 * @returns {number}
 */
function calculateTotal(items) {
  return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}

// Étape 2 : Convertir en TypeScript
interface CartItem {
  price: number;
  quantity: number;
}

function calculateTotal(items: CartItem[]): number {
  return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}

// Étape 3 : Ajouter validation runtime (défense en profondeur)
function calculateTotal(items: CartItem[]): number {
  if (!Array.isArray(items)) {
    throw new Error('Items must be an array');
  }

  return items.reduce((sum, item) => {
    if (typeof item.price !== 'number' || typeof item.quantity !== 'number') {
      throw new Error('Invalid item format');
    }
    return sum + item.price * item.quantity;
  }, 0);
}

TypeScript avec les Frameworks Populaires

React + TypeScript

import React, { useState, useEffect } from 'react';

// Props typées
interface UserCardProps {
  user: {
    id: string;
    name: string;
    email: string;
    avatar?: string;
  };
  onEdit?: (userId: string) => void;
  onDelete?: (userId: string) => void;
}

// Composant fonctionnel typé
export function UserCard({ user, onEdit, onDelete }: UserCardProps) {
  const [isHovered, setIsHovered] = useState<boolean>(false);

  useEffect(() => {
    console.log('User card mounted:', user.id);

    return () => {
      console.log('User card unmounted:', user.id);
    };
  }, [user.id]);

  const handleEdit = () => {
    onEdit?.(user.id); // Opérateur de navigation sécurisée
  };

  return (
    <div
      onMouseEnter={() => setIsHovered(true)}
      onMouseLeave={() => setIsHovered(false)}
      className={isHovered ? 'card-hovered' : 'card'}
    >
      {user.avatar && <img src={user.avatar} alt={user.name} />}
      <h3>{user.name}</h3>
      <p>{user.email}</p>

      <button onClick={handleEdit}>Edit</button>
      {onDelete && (
        <button onClick={() => onDelete(user.id)}>Delete</button>
      )}
    </div>
  );
}

// Hooks personnalisés typés
function useUser(userId: string) {
  const [user, setUser] = useState<User | null>(null);
  const [loading, setLoading] = useState<boolean>(true);
  const [error, setError] = useState<Error | null>(null);

  useEffect(() => {
    let cancelled = false;

    async function fetchUser() {
      try {
        setLoading(true);
        const response = await fetch(`/api/users/${userId}`);
        const data = await response.json();

        if (!cancelled) {
          setUser(data);
        }
      } catch (err) {
        if (!cancelled) {
          setError(err as Error);
        }
      } finally {
        if (!cancelled) {
          setLoading(false);
        }
      }
    }

    fetchUser();

    return () => {
      cancelled = true;
    };
  }, [userId]);

  return { user, loading, error };
}

Next.js + TypeScript

// app/page.tsx (App Router)
import { Metadata } from 'next';

export const metadata: Metadata = {
  title: 'Home Page',
  description: 'Welcome to my site'
};

interface HomePageProps {
  searchParams: { [key: string]: string | string[] | undefined };
}

export default async function HomePage({ searchParams }: HomePageProps) {
  // Server component typé
  const data = await fetchData();

  return (
    <div>
      <h1>Welcome</h1>
      <DataDisplay data={data} />
    </div>
  );
}

// API Route typée
// app/api/users/route.ts
import { NextRequest, NextResponse } from 'next/server';

interface UserPayload {
  name: string;
  email: string;
}

export async function POST(request: NextRequest) {
  try {
    const body: UserPayload = await request.json();

    // Validation
    if (!body.name || !body.email) {
      return NextResponse.json(
        { error: 'Missing required fields' },
        { status: 400 }
      );
    }

    // Créer utilisateur
    const user = await createUser(body);

    return NextResponse.json(user, { status: 201 });
  } catch (error) {
    return NextResponse.json(
      { error: 'Internal server error' },
      { status: 500 }
    );
  }
}

Outils Essentiels de l'Écosystème TypeScript

// 1. ESLint + TypeScript
// .eslintrc.js
module.exports = {
  parser: '@typescript-eslint/parser',
  plugins: ['@typescript-eslint'],
  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:@typescript-eslint/recommended-requiring-type-checking'
  ],
  parserOptions: {
    project: './tsconfig.json'
  },
  rules: {
    '@typescript-eslint/no-unused-vars': 'error',
    '@typescript-eslint/no-explicit-any': 'warn',
    '@typescript-eslint/explicit-function-return-type': 'off'
  }
};

// 2. Prettier
// .prettierrc
{
  "semi": true,
  "singleQuote": true,
  "tabWidth": 2,
  "printWidth": 100
}

// 3. Husky + lint-staged (hooks pre-commit)
// package.json
{
  "lint-staged": {
    "*.{ts,tsx}": [
      "eslint --fix",
      "prettier --write"
    ]
  }
}

// 4. ts-node pour exécution directe
// Exécuter TypeScript directement sans compiler
npx ts-node script.ts

// 5. Test de types avec tsd
import { expectType } from 'tsd';

// Tester si les types sont corrects
expectType<number>(calculateTotal([{ price: 10, quantity: 2 }]));

Conclusion : TypeScript Vaut-il l'Investissement ?

Oui, absolument. TypeScript n'est plus une tendance — c'est le nouveau standard pour JavaScript professionnel en 2025.

Bénéfices concrets :

  • Moins de bugs : Erreurs détectées avant la production
  • Meilleure DX : Autocomplétion et refactoring sécurisés
  • Code plus maintenable : Documentation vivante via les types
  • Carrière : 68% des offres exigent TypeScript

Si vous voulez maîtriser JavaScript et TypeScript de manière structurée, je vous recommande de consulter un autre article : Programmation Fonctionnelle en JavaScript : Comprendre les Higher-Order Functions où vous découvrirez des patterns qui fonctionnent parfaitement avec TypeScript.

C'est parti ! 🦅

📚 Vous Voulez Approfondir Vos Connaissances en JavaScript ?

Cet article a couvert TypeScript, mais maîtriser JavaScript solide est la base de tout.

Les développeurs qui investissent dans des connaissances structurées ont tendance à avoir plus d'opportunités sur le marché.

Matériel d'Étude Complet

Si vous voulez maîtriser JavaScript du basique à l'avancé, j'ai préparé un guide complet :

Options d'investissement :

  • €9,90 (paiement unique)

👉 Découvrir le Guide JavaScript

💡 Matériel mis à jour avec les meilleures pratiques du marché

Commentaires (0)

Cet article n'a pas encore de commentaires. Soyez le premier!

Ajouter des commentaires