0
# Error Handling
1
2
Comprehensive error system for handling various formatting failures including missing values, invalid types, and missing Intl APIs.
3
4
## Capabilities
5
6
### ErrorCode Enum
7
8
Defines the types of errors that can occur during message formatting.
9
10
```typescript { .api }
11
/**
12
* Error codes for different types of formatting failures
13
*/
14
enum ErrorCode {
15
/** When a placeholder has no corresponding value provided */
16
MISSING_VALUE = 'MISSING_VALUE',
17
/** When a provided value is invalid for the placeholder type */
18
INVALID_VALUE = 'INVALID_VALUE',
19
/** When a required Intl API is not available in the environment */
20
MISSING_INTL_API = 'MISSING_INTL_API'
21
}
22
```
23
24
### FormatError Class
25
26
Base error class for all formatting-related errors.
27
28
```typescript { .api }
29
/**
30
* Base class for formatting errors
31
*/
32
class FormatError extends Error {
33
/** Error code identifying the type of error */
34
readonly code: ErrorCode;
35
/** Original message string being formatted (if available) */
36
readonly originalMessage: string | undefined;
37
38
/**
39
* Create a new FormatError
40
* @param msg - Error message
41
* @param code - Error code
42
* @param originalMessage - Original message string being formatted
43
*/
44
constructor(msg: string, code: ErrorCode, originalMessage?: string);
45
46
/**
47
* Custom string representation
48
* @returns Formatted error string with code
49
*/
50
toString(): string;
51
}
52
```
53
54
**Usage Examples:**
55
56
```typescript
57
import { FormatError, ErrorCode } from "intl-messageformat";
58
59
try {
60
const msg = new IntlMessageFormat('Hello {name}!', 'en-US');
61
msg.format({}); // Missing 'name' value
62
} catch (error) {
63
if (error instanceof FormatError) {
64
console.log(error.code); // ErrorCode.MISSING_VALUE
65
console.log(error.originalMessage); // 'Hello {name}!'
66
console.log(error.toString()); // "[formatjs Error: MISSING_VALUE] ..."
67
}
68
}
69
```
70
71
### MissingValueError Class
72
73
Thrown when a placeholder in the message has no corresponding value in the provided data.
74
75
```typescript { .api }
76
/**
77
* Error thrown when a required placeholder value is missing
78
*/
79
class MissingValueError extends FormatError {
80
/**
81
* Create a new MissingValueError
82
* @param variableId - Name of the missing variable
83
* @param originalMessage - Original message string being formatted
84
*/
85
constructor(variableId: string, originalMessage?: string);
86
}
87
```
88
89
**Usage Examples:**
90
91
```typescript
92
import IntlMessageFormat from "intl-messageformat";
93
94
try {
95
const msg = new IntlMessageFormat('Welcome {firstName} {lastName}!', 'en-US');
96
msg.format({ firstName: 'John' }); // Missing lastName
97
} catch (error) {
98
console.log(error.message);
99
// "The intl string context variable "lastName" was not provided to the string "Welcome {firstName} {lastName}!""
100
}
101
```
102
103
### InvalidValueError Class
104
105
Thrown when a value provided for a placeholder is not valid for that placeholder type (e.g., wrong option in a select statement).
106
107
```typescript { .api }
108
/**
109
* Error thrown when a provided value is invalid for the placeholder
110
*/
111
class InvalidValueError extends FormatError {
112
/**
113
* Create a new InvalidValueError
114
* @param variableId - Name of the variable with invalid value
115
* @param value - The invalid value that was provided
116
* @param options - Valid options for this variable
117
* @param originalMessage - Original message string being formatted
118
*/
119
constructor(
120
variableId: string,
121
value: any,
122
options: string[],
123
originalMessage?: string
124
);
125
}
126
```
127
128
**Usage Examples:**
129
130
```typescript
131
import IntlMessageFormat from "intl-messageformat";
132
133
try {
134
const msg = new IntlMessageFormat(
135
'{status, select, active {Active} inactive {Inactive} other {Unknown}}',
136
'en-US'
137
);
138
msg.format({ status: 'pending' }); // 'pending' not in options
139
} catch (error) {
140
console.log(error.message);
141
// 'Invalid values for "status": "pending". Options are "active", "inactive", "other"'
142
}
143
```
144
145
### InvalidValueTypeError Class
146
147
Thrown when a value is of the wrong type for a placeholder (e.g., providing a string when a function is expected for XML tags).
148
149
```typescript { .api }
150
/**
151
* Error thrown when a value has the wrong type for the placeholder
152
*/
153
class InvalidValueTypeError extends FormatError {
154
/**
155
* Create a new InvalidValueTypeError
156
* @param value - The variable name with wrong type
157
* @param type - The expected type
158
* @param originalMessage - Original message string being formatted
159
*/
160
constructor(value: any, type: string, originalMessage?: string);
161
}
162
```
163
164
**Usage Examples:**
165
166
```typescript
167
import IntlMessageFormat from "intl-messageformat";
168
169
try {
170
const msg = new IntlMessageFormat(
171
'Click <link>here</link> to continue.',
172
'en-US'
173
);
174
msg.format({ link: 'not-a-function' }); // Should be a function
175
} catch (error) {
176
console.log(error.message);
177
// 'Value for "not-a-function" must be of type function'
178
}
179
```
180
181
## Error Handling Patterns
182
183
### Basic Error Handling
184
185
```typescript
186
import IntlMessageFormat, { FormatError, ErrorCode } from "intl-messageformat";
187
188
function safeFormat(messageString: string, values: Record<string, any>, locale = 'en-US') {
189
try {
190
const msg = new IntlMessageFormat(messageString, locale);
191
return msg.format(values);
192
} catch (error) {
193
if (error instanceof FormatError) {
194
switch (error.code) {
195
case ErrorCode.MISSING_VALUE:
196
console.warn('Missing value:', error.message);
197
return messageString; // Fallback to original
198
case ErrorCode.INVALID_VALUE:
199
console.warn('Invalid value:', error.message);
200
return messageString;
201
case ErrorCode.MISSING_INTL_API:
202
console.error('Missing Intl API:', error.message);
203
return messageString;
204
default:
205
console.error('Format error:', error.message);
206
return messageString;
207
}
208
}
209
throw error; // Re-throw non-format errors
210
}
211
}
212
```
213
214
### Validation Before Formatting
215
216
```typescript
217
function validateMessageValues(
218
messageString: string,
219
values: Record<string, any>,
220
locale = 'en-US'
221
): { isValid: boolean; errors: string[] } {
222
const errors: string[] = [];
223
224
try {
225
const msg = new IntlMessageFormat(messageString, locale);
226
msg.format(values);
227
return { isValid: true, errors: [] };
228
} catch (error) {
229
if (error instanceof FormatError) {
230
errors.push(error.message);
231
return { isValid: false, errors };
232
}
233
throw error;
234
}
235
}
236
237
// Usage
238
const validation = validateMessageValues(
239
'Hello {name}, you have {count, plural, one {# message} other {# messages}}.',
240
{ name: 'Alice' } // Missing count
241
);
242
if (!validation.isValid) {
243
console.log('Validation errors:', validation.errors);
244
}
245
```
246
247
### Development vs Production Error Handling
248
249
```typescript
250
const isDevelopment = process.env.NODE_ENV === 'development';
251
252
function formatMessage(messageString: string, values: Record<string, any>) {
253
try {
254
const msg = new IntlMessageFormat(messageString, 'en-US');
255
return msg.format(values);
256
} catch (error) {
257
if (isDevelopment) {
258
// In development, show detailed errors
259
console.error('Message formatting failed:', {
260
message: messageString,
261
values,
262
error: error.message
263
});
264
return `[FORMAT ERROR: ${error.message}]`;
265
} else {
266
// In production, fail silently with fallback
267
console.warn('Message formatting failed silently');
268
return messageString;
269
}
270
}
271
}
272
```
273
274
## Error Prevention
275
276
### Type-Safe Value Objects
277
278
```typescript
279
// Define interfaces for your message values
280
interface UserMessage {
281
name: string;
282
count: number;
283
status: 'active' | 'inactive';
284
}
285
286
function formatUserMessage(values: UserMessage) {
287
const msg = new IntlMessageFormat(
288
'Hello {name}! You have {count, number} items. Status: {status, select, active {Active} inactive {Inactive} other {Unknown}}',
289
'en-US'
290
);
291
return msg.format(values);
292
}
293
294
// This will catch type errors at compile time
295
formatUserMessage({
296
name: 'Alice',
297
count: 5,
298
status: 'active' // TypeScript ensures this is valid
299
});
300
```
301
302
### Message Validation Utility
303
304
```typescript
305
class MessageValidator {
306
static validatePlaceholders(
307
messageString: string,
308
providedValues: Record<string, any>
309
): { missing: string[]; extra: string[] } {
310
// Extract placeholders from message (simplified)
311
const placeholders = messageString.match(/{([^}]+)}/g)?.map(
312
match => match.slice(1, -1).split(',')[0].trim()
313
) || [];
314
315
const provided = Object.keys(providedValues);
316
const missing = placeholders.filter(p => !provided.includes(p));
317
const extra = provided.filter(p => !placeholders.includes(p));
318
319
return { missing, extra };
320
}
321
}
322
323
// Usage
324
const validation = MessageValidator.validatePlaceholders(
325
'Hello {name}!',
326
{ name: 'Alice', age: 25 }
327
);
328
console.log(validation); // { missing: [], extra: ['age'] }
329
```