0
# Web APIs and DOM
1
2
Type checking for web-specific APIs including DOM elements, form data, URL objects, and browser-specific types.
3
4
## Capabilities
5
6
### HTML Element Checking
7
8
Check if a value is an HTML DOM element.
9
10
```typescript { .api }
11
/**
12
* Check if value is an HTML element
13
* @param value - Value to check
14
* @returns True if value is HTMLElement
15
*/
16
function isHtmlElement(value: unknown): value is HTMLElement;
17
```
18
19
**Usage Examples:**
20
21
```typescript
22
import is from '@sindresorhus/is';
23
24
// DOM elements
25
const div = document.createElement('div');
26
const button = document.querySelector('button');
27
const body = document.body;
28
29
is.htmlElement(div); // => true
30
is.htmlElement(button); // => true (if found)
31
is.htmlElement(body); // => true
32
33
// Non-elements
34
is.htmlElement(document); // => false
35
is.htmlElement(window); // => false
36
is.htmlElement('div'); // => false
37
38
// Type guard usage
39
function attachEventListener(element: unknown, event: string, handler: Function) {
40
if (is.htmlElement(element)) {
41
// element is now typed as HTMLElement
42
element.addEventListener(event, handler as EventListener);
43
return () => element.removeEventListener(event, handler as EventListener);
44
}
45
throw new Error('Element must be an HTMLElement');
46
}
47
48
// DOM manipulation
49
function setElementContent(element: unknown, content: string) {
50
if (is.htmlElement(element)) {
51
element.textContent = content;
52
element.style.display = 'block';
53
}
54
}
55
```
56
57
### Blob Type Checking
58
59
Check if a value is a Blob object.
60
61
```typescript { .api }
62
/**
63
* Check if value is a Blob
64
* @param value - Value to check
65
* @returns True if value is Blob
66
*/
67
function isBlob(value: unknown): value is Blob;
68
```
69
70
**Usage Examples:**
71
72
```typescript
73
import is from '@sindresorhus/is';
74
75
// Creating blobs
76
const textBlob = new Blob(['Hello, World!'], { type: 'text/plain' });
77
const jsonBlob = new Blob([JSON.stringify({data: 'test'})], { type: 'application/json' });
78
79
is.blob(textBlob); // => true
80
is.blob(jsonBlob); // => true
81
82
// File objects (inherit from Blob)
83
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
84
const file = fileInput?.files?.[0];
85
if (file) {
86
is.blob(file); // => true (File extends Blob)
87
}
88
89
// Type guard usage
90
async function processBlob(data: unknown) {
91
if (is.blob(data)) {
92
// data is now typed as Blob
93
console.log('Blob size:', data.size);
94
console.log('Blob type:', data.type);
95
96
const text = await data.text();
97
console.log('Blob content:', text);
98
}
99
}
100
101
// File upload handling
102
function handleFileUpload(file: unknown) {
103
if (is.blob(file)) {
104
const formData = new FormData();
105
formData.append('file', file);
106
return formData;
107
}
108
throw new Error('Invalid file provided');
109
}
110
```
111
112
### FormData Type Checking
113
114
Check if a value is a FormData object.
115
116
```typescript { .api }
117
/**
118
* Check if value is FormData
119
* @param value - Value to check
120
* @returns True if value is FormData
121
*/
122
function isFormData(value: unknown): value is FormData;
123
```
124
125
**Usage Examples:**
126
127
```typescript
128
import is from '@sindresorhus/is';
129
130
const formData = new FormData();
131
formData.append('name', 'John');
132
formData.append('age', '30');
133
134
is.formData(formData); // => true
135
is.formData({}); // => false
136
is.formData(new URLSearchParams()); // => false
137
138
// Type guard usage
139
function submitForm(data: unknown) {
140
if (is.formData(data)) {
141
// data is now typed as FormData
142
for (const [key, value] of data.entries()) {
143
console.log(`${key}: ${value}`);
144
}
145
146
return fetch('/api/submit', {
147
method: 'POST',
148
body: data
149
});
150
}
151
throw new Error('Data must be FormData');
152
}
153
154
// Form processing
155
function processFormSubmission(form: HTMLFormElement, additionalData: Record<string, string>) {
156
const formData = new FormData(form);
157
158
// Add additional data
159
Object.entries(additionalData).forEach(([key, value]) => {
160
formData.append(key, value);
161
});
162
163
if (is.formData(formData)) {
164
return submitForm(formData);
165
}
166
}
167
```
168
169
### URL Instance Checking
170
171
Check if a value is a URL object instance.
172
173
```typescript { .api }
174
/**
175
* Check if value is a URL instance
176
* @param value - Value to check
177
* @returns True if value is URL instance
178
*/
179
function isUrlInstance(value: unknown): value is URL;
180
```
181
182
**Usage Examples:**
183
184
```typescript
185
import is from '@sindresorhus/is';
186
187
const url = new URL('https://example.com/path?query=value');
188
const invalidUrl = 'https://example.com';
189
190
is.urlInstance(url); // => true
191
is.urlInstance(invalidUrl); // => false (string, not URL instance)
192
193
// Type guard usage
194
function processUrl(url: unknown) {
195
if (is.urlInstance(url)) {
196
// url is now typed as URL
197
console.log('Protocol:', url.protocol);
198
console.log('Host:', url.host);
199
console.log('Pathname:', url.pathname);
200
console.log('Search params:', url.searchParams.toString());
201
202
return {
203
protocol: url.protocol,
204
host: url.host,
205
path: url.pathname,
206
query: Object.fromEntries(url.searchParams)
207
};
208
}
209
throw new Error('Invalid URL instance');
210
}
211
212
// URL manipulation
213
function addQueryParams(url: unknown, params: Record<string, string>) {
214
if (is.urlInstance(url)) {
215
Object.entries(params).forEach(([key, value]) => {
216
url.searchParams.set(key, value);
217
});
218
return url.toString();
219
}
220
throw new Error('URL must be a URL instance');
221
}
222
```
223
224
### URLSearchParams Type Checking
225
226
Check if a value is a URLSearchParams object.
227
228
```typescript { .api }
229
/**
230
* Check if value is URLSearchParams
231
* @param value - Value to check
232
* @returns True if value is URLSearchParams
233
*/
234
function isUrlSearchParams(value: unknown): value is URLSearchParams;
235
```
236
237
**Usage Examples:**
238
239
```typescript
240
import is from '@sindresorhus/is';
241
242
const params = new URLSearchParams('?name=John&age=30');
243
const paramsFromObject = new URLSearchParams({ city: 'NYC', country: 'US' });
244
245
is.urlSearchParams(params); // => true
246
is.urlSearchParams(paramsFromObject); // => true
247
is.urlSearchParams('?name=John&age=30'); // => false (string)
248
249
// Type guard usage
250
function processSearchParams(params: unknown) {
251
if (is.urlSearchParams(params)) {
252
// params is now typed as URLSearchParams
253
const result: Record<string, string> = {};
254
255
for (const [key, value] of params.entries()) {
256
result[key] = value;
257
}
258
259
return result;
260
}
261
throw new Error('Invalid URLSearchParams');
262
}
263
264
// Query string manipulation
265
function buildQueryString(params: unknown, additionalParams: Record<string, string>) {
266
let searchParams: URLSearchParams;
267
268
if (is.urlSearchParams(params)) {
269
searchParams = new URLSearchParams(params);
270
} else {
271
searchParams = new URLSearchParams();
272
}
273
274
Object.entries(additionalParams).forEach(([key, value]) => {
275
searchParams.set(key, value);
276
});
277
278
return searchParams.toString();
279
}
280
```
281
282
## Usage Patterns
283
284
### DOM Event Handling
285
286
```typescript
287
import is from '@sindresorhus/is';
288
289
function setupEventDelegation(container: unknown, selector: string, handler: Function) {
290
if (!is.htmlElement(container)) {
291
throw new Error('Container must be an HTML element');
292
}
293
294
container.addEventListener('click', (event) => {
295
const target = event.target;
296
297
if (is.htmlElement(target) && target.matches(selector)) {
298
handler(event, target);
299
}
300
});
301
}
302
303
// Usage
304
setupEventDelegation(document.body, '.button', (event, button) => {
305
console.log('Button clicked:', button.textContent);
306
});
307
```
308
309
### File Upload Processing
310
311
```typescript
312
import is from '@sindresorhus/is';
313
314
async function handleFileUpload(file: unknown): Promise<FormData> {
315
if (!is.blob(file)) {
316
throw new Error('File must be a Blob or File');
317
}
318
319
// Validate file size (10MB limit)
320
if (file.size > 10 * 1024 * 1024) {
321
throw new Error('File too large (max 10MB)');
322
}
323
324
// Validate file type
325
if (!file.type.startsWith('image/')) {
326
throw new Error('Only image files are allowed');
327
}
328
329
const formData = new FormData();
330
formData.append('file', file);
331
formData.append('timestamp', Date.now().toString());
332
333
return formData;
334
}
335
336
// Drag and drop handling
337
function setupDropZone(element: unknown) {
338
if (!is.htmlElement(element)) {
339
throw new Error('Drop zone must be an HTML element');
340
}
341
342
element.addEventListener('drop', async (event) => {
343
event.preventDefault();
344
345
const files = event.dataTransfer?.files;
346
if (files) {
347
for (const file of Array.from(files)) {
348
try {
349
const formData = await handleFileUpload(file);
350
console.log('File ready for upload:', formData);
351
} catch (error) {
352
console.error('File upload error:', error);
353
}
354
}
355
}
356
});
357
}
358
```
359
360
### URL and Query Parameter Handling
361
362
```typescript
363
import is from '@sindresorhus/is';
364
365
function parseUrl(input: unknown): {url: URL; params: Record<string, string>} {
366
let url: URL;
367
368
if (is.urlInstance(input)) {
369
url = input;
370
} else if (is.string(input)) {
371
try {
372
url = new URL(input);
373
} catch {
374
throw new Error('Invalid URL string');
375
}
376
} else {
377
throw new Error('Input must be URL instance or string');
378
}
379
380
const params: Record<string, string> = {};
381
for (const [key, value] of url.searchParams.entries()) {
382
params[key] = value;
383
}
384
385
return { url, params };
386
}
387
388
function buildApiUrl(baseUrl: string, endpoint: string, params?: unknown): string {
389
const url = new URL(endpoint, baseUrl);
390
391
if (params) {
392
let searchParams: URLSearchParams;
393
394
if (is.urlSearchParams(params)) {
395
searchParams = params;
396
} else if (is.plainObject(params)) {
397
searchParams = new URLSearchParams(params as Record<string, string>);
398
} else {
399
throw new Error('Params must be URLSearchParams or plain object');
400
}
401
402
url.search = searchParams.toString();
403
}
404
405
return url.toString();
406
}
407
```
408
409
### Form Data Processing
410
411
```typescript
412
import is from '@sindresorhus/is';
413
414
function extractFormData(form: unknown): Record<string, string | File> {
415
if (!is.htmlElement(form) || form.tagName !== 'FORM') {
416
throw new Error('Input must be a form element');
417
}
418
419
const formData = new FormData(form as HTMLFormElement);
420
const result: Record<string, string | File> = {};
421
422
for (const [key, value] of formData.entries()) {
423
if (is.string(value)) {
424
result[key] = value;
425
} else if (is.blob(value)) {
426
result[key] = value as File;
427
}
428
}
429
430
return result;
431
}
432
433
function validateFormData(data: unknown): FormData {
434
if (!is.formData(data)) {
435
throw new Error('Input must be FormData');
436
}
437
438
// Validate required fields
439
const requiredFields = ['name', 'email'];
440
for (const field of requiredFields) {
441
if (!data.has(field)) {
442
throw new Error(`Missing required field: ${field}`);
443
}
444
}
445
446
return data;
447
}
448
```
449
450
## Notes
451
452
- HTML element checking validates DOM elements but not other node types (text, comment, etc.)
453
- Blob checking includes File objects since File extends Blob
454
- FormData is useful for multipart form submissions and file uploads
455
- URL instance checking validates URL objects, not URL strings (use `isUrlString()` for strings)
456
- URLSearchParams provides easy query string manipulation
457
- Web API checks are browser-specific and may not work in Node.js environments
458
- All web API checks work with TypeScript type guards for compile-time type narrowing
459
- Consider environment detection before using these checks in universal/isomorphic code