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'); // ErreurInterfaces 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é

