[object Promise] en TypeScript peut sembler mystérieux au premier abord, mais il devient encore plus déroutant quand il génère des erreurs incompréhensibles. Si vous avez déjà vu des messages comme "Type 'string' is not assignable to type 'never'" et que vous vous êtes demandé ce qui se passait, cet article est fait pour vous.
Nous allons explorer ensemble les erreurs les plus courantes liées au type never, comprendre leur origine et surtout apprendre à les résoudre efficacement.
Pourquoi le type Never génère-t-il autant d'erreurs ?
Le type never représente une valeur qui ne devrait jamais exister. Il est souvent le résultat d'un système de types qui a déterminé qu'une branche de code est inaccessible. Quand TypeScript infère never alors que vous attendez autre chose, c'est généralement le signe d'une incohérence dans votre typage.
Les 5 erreurs Never les plus courantes
1. "Type 'string' is not assignable to type 'never'"
C'est probablement l'erreur la plus frustrante. Elle survient souvent dans ce type de situation :
function processValue(value: string | number) {
if (typeof value === 'string') {
} else if (typeof value === 'number') {
} else {
console.log(value);
}
}
Solution : Le problème vient du fait que TypeScript a correctement déterminé que la branche else est inaccessible. Si vous voulez vraiment gérer ce cas, ajoutez une assertion de type ou revoyez votre logique :
2. Arrays qui deviennent never[]
Quand vous déclarez un array vide sans spécifier son type, TypeScript peut l'inférer comme never[] :
const items = [];
items.push('hello');
Solutions :
Il existe plusieurs solutions pour cela avec un typage explicite, une assertion, ou une initialisation de l'array
const items: string[] = [];
const items = [] as string[];
const items = ['premier-item'];
3. Erreurs dans les unions exhaustives
Cette erreur survient souvent avec les switch statements sur des unions de types :
type Status = 'pending' | 'approved' | 'rejected';
function handleStatus(status: Status) {
switch (status) {
case 'pending':
return 'En attente';
case 'approved':
return 'Approuvé';
default:
throw new Error(`Status non géré: ${status}`);
}
}
Solution avec exhaustive checking :
function handleStatus(status: Status): string {
switch (status) {
case 'pending':
return 'En attente';
case 'approved':
return 'Approuvé';
case 'rejected':
return 'Rejeté';
default:
const exhaustiveCheck: never = status;
throw new Error(`Status non géré: ${exhaustiveCheck}`);
}
}
4. "Function lacks ending return statement"
Cette erreur apparaît quand TypeScript détecte qu'une fonction pourrait ne pas retourner de valeur :
function getStatusMessage(status: 'active' | 'inactive'): string {
if (status === 'active') {
return 'Actif';
}
}
Solutions
function getStatusMessage(status: 'active' | 'inactive'): string {
if (status === 'active') {
return 'Actif';
} else {
return 'Inactif';
}
}
function getStatusMessage(status: 'active' | 'inactive'): string {
switch (status) {
case 'active':
return 'Actif';
case 'inactive':
return 'Inactif';
default:
const exhaustiveCheck: never = status;
throw new Error(`Status non géré: ${exhaustiveCheck}`);
}
}
1. Analyser le message d'erreur
TypeScript donne généralement des indices précieux dans ses messages d'erreur. Cherchez :
- Le type attendu vs le type fourni
- La ligne exacte où l'erreur survient
- Le contexte de l'inférence de type
2. Utiliser les outils de développement
TypeScript Playground : Collez votre code sur typescriptlang.org/play pour voir les types inférés en temps réel.
IDE avec hover : Survolez vos variables pour voir leur type inféré :
function example(value: string | number) {
if (typeof value === 'string') {
} else {
}
}
3. Technique de narrowing progressif
Ajoutez des assertions de type ou des logs temporaires pour comprendre où l'inférence se casse :
function debug(value: unknown) {
console.log('Type initial:', typeof value);
if (typeof value === 'string') {
console.log('Dans la branche string');
} else if (typeof value === 'number') {
console.log('Dans la branche number');
} else {
console.log('Dans else, type:', typeof value);
}
}
Solutions concrètes et bonnes pratiques
Refactoring des unions complexes
Pour les unions complexes, préférez les discriminated unions :
type ApiResponse = {
data?: any;
error?: string;
status: number;
}
type ApiResponse =
| { success: true; data: any; status: number }
| { success: false; error: string; status: number };
function handleResponse(response: ApiResponse) {
if (response.success) {
console.log(response.data);
} else {
console.log(response.error);
}
}
Utiliser des assertions de type avec parcimonie
Les assertions de type peuvent masquer les vrais problèmes :
const value = getData() as string;
const rawValue = getData();
if (typeof rawValue === 'string') {
const value = rawValue;
}
Configuration TypeScript recommandée
Dans votre tsconfig.json, activez ces options pour détecter les problèmes plus tôt
{
"compilerOptions": {
"strict": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noImplicitAny": true,
"strictNullChecks": true
}
}
Conclusion
Les erreurs liées au type never en TypeScript sont souvent le symptôme d'un problème plus profond dans la conception de vos types. En comprenant leur origine et en appliquant les bonnes pratiques présentées dans cet article, vous pourrez non seulement les résoudre mais aussi les prévenir.
Le type never n'est pas votre ennemi : c'est un outil puissant pour créer du code plus robuste. Une fois maîtrisé, il devient un allié précieux pour détecter les incohérences logiques dans votre code.
Points clés à retenir :
- Les erreurs
never signalent souvent des incohérences logiques - Utilisez les outils de debug (hover, TypeScript Playground)
- Préférez les discriminated unions pour les types complexes
- L'exhaustive checking vous protège des oublis
- Une configuration TypeScript stricte détecte les problèmes plus tôt
En maîtrisant ces concepts, vous transformerez les erreurs TypeScript frustrantes en opportunités d'améliorer la qualité de votre code.