Les generics TypeScript sont le concept qui fait passer d’un TypeScript « fonctionnel » à un TypeScript vraiment typé. Beaucoup de développeurs les évitent ou copient des patterns sans les comprendre. Ce guide les démystifie avec des exemples concrets — pas des exemples académiques déconnectés de la réalité.
Pourquoi les generics existent
Sans generics, vous avez deux options mauvaises : any (perd tout typage) ou dupliquer le code pour chaque type. Les generics permettent d’écrire du code réutilisable qui conserve l’information de type.
// Sans generics — on duplique ou on perd le type
function firstElement(arr: number[]): number { return arr[0] }
function firstElementStr(arr: string[]): string { return arr[0] }
// Avec generics — une seule fonction, type préservé
function firstElement(arr: T[]): T {
return arr[0]
}
const num = firstElement([1, 2, 3]) // TypeScript sait que num: number
const str = firstElement(['a', 'b']) // TypeScript sait que str: string
La syntaxe de base
// T est une convention — vous pouvez utiliser n'importe quel nom
// T = Type, K = Key, V = Value, E = Element sont les conventions courantes
// Fonction générique
function identity(value: T): T {
return value
}
// Interface générique
interface ApiResponse {
data: T
status: number
message: string
}
// Type générique
type Nullable = T | null
type Optional = T | undefined
// Utilisation
const response: ApiResponse = await fetchUsers()
const maybeUser: Nullable = getUser(id)
Les contraintes — extends
Parfois, vous avez besoin que le type générique ait certaines propriétés. extends permet de contraindre T :
// T doit avoir une propriété id
function getById(items: T[], id: string): T | undefined {
return items.find(item => item.id === id)
}
// Fonctionne avec n'importe quel objet qui a un id
getById(users, '123') // ✅ User a un id
getById(products, '456') // ✅ Product a un id
getById([1, 2, 3], '1') // ❌ number n'a pas de id
// Contrainte avec keyof — accéder à une propriété de façon type-safe
function getProperty(obj: T, key: K): T[K] {
return obj[key]
}
const user = { name: 'Alice', age: 30 }
getProperty(user, 'name') // string — type inféré correctement
getProperty(user, 'age') // number
getProperty(user, 'xyz') // ❌ Erreur de compilation — 'xyz' n'est pas une clé de user
Les generics en React
Les generics sont très utiles pour typer correctement les composants et hooks React :
// Hook générique pour fetcher des données
function useFetch(url: string) {
const [data, setData] = useState(null)
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
useEffect(() => {
fetch(url)
.then(r => r.json())
.then((d: T) => setData(d))
.catch(setError)
.finally(() => setLoading(false))
}, [url])
return { data, loading, error }
}
// Usage avec le type inféré
const { data: users } = useFetch('/api/users')
// users est typé User[] | null — pas any
// Composant générique pour liste
interface ListProps {
items: T[]
renderItem: (item: T) => React.ReactNode
keyExtractor: (item: T) => string
}
function List({ items, renderItem, keyExtractor }: ListProps) {
return (
{items.map(item => (
- {renderItem(item)}
))}
)
}
// Usage
{user.name}}
keyExtractor={user => user.id}
/>
Les utility types — les generics déjà fournis
TypeScript fournit des types utilitaires génériques prêts à l’emploi. Ce sont les plus utilisés en pratique :
interface User {
id: string
name: string
email: string
role: 'admin' | 'user'
}
// Partial — toutes les propriétés deviennent optionnelles
type UserUpdate = Partial
// { id?: string; name?: string; email?: string; role?: ... }
// Required — toutes les propriétés deviennent requises
type RequiredUser = Required>
// Pick — sélectionner des propriétés
type UserPreview = Pick
// { id: string; name: string }
// Omit — exclure des propriétés
type UserWithoutId = Omit
// { name: string; email: string; role: ... }
// Record — créer un type objet avec clés et valeurs typés
type RolePermissions = Record<'admin' | 'user', string[]>
// ReturnType — extraire le type de retour d'une fonction
type FetchResult = ReturnType
// Parameters — extraire les paramètres d'une fonction
type FetchParams = Parameters
L’erreur la plus courante
Ne pas inférer le type quand TypeScript peut le faire seul :
// ❌ Redondant — TypeScript infère T = string depuis l'argument
const result = identity('hello')
// ✅ TypeScript infère T automatiquement
const result = identity('hello') // T = string, inféré
// ❌ Trop de generics — si vous en avez 4+, c'est souvent un signe de sur-ingénierie
function doThing(a: A, b: B): C { ... }
// ✅ Simplifier avec des types plus spécifiques si possible
Offres TypeScript et React
Des postes pour développeurs TypeScript — les équipes qui codent avec des types stricts et qui vous feront progresser.
À lire aussi : TypeScript pour développeurs JS — Hooks React — React vs Vue vs Angular