You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: book-content/chapters/16-the-utils-folder.md
+38-19Lines changed: 38 additions & 19 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -300,12 +300,6 @@ const result: Omit<
300
300
301
301
Note how clever TypeScript is being here. Even though we didn't specify a return type for `removeId`, TypeScript has inferred that `result` is an object with all the properties of the input object, except `id`.
302
302
303
-
<!-- CONTINUE -->
304
-
305
-
### Type Safe vs Type Faith
306
-
307
-
<!-- TODO -->
308
-
309
303
## Type Predicates
310
304
311
305
A type predicate is a special return type that tells TypeScript that a function returns a Boolean value that says something about the type of one of its parameters.
@@ -315,7 +309,7 @@ For example, say we want to ensure that a variable is an `Album` before we try a
315
309
We can write an `isAlbum` function that takes in an input, and specifies a return type of `input is Album`. The body of the function will check that the `input` passed in is a non-null object with the required properties of an `Album`:
316
310
317
311
```typescript
318
-
function isAlbum(input):inputisAlbum {
312
+
function isAlbum(input:unknown):inputisAlbum {
319
313
return (
320
314
typeofinput==="object"&&
321
315
input!==null&&
@@ -361,6 +355,8 @@ let notAnAlbumTitle = getAlbumTitle("Some string");
361
355
console.log(notAnAlbumTitle); // "Unknown Album"
362
356
```
363
357
358
+
### Type Predicates Can be Unsafe
359
+
364
360
Type predicates are a great technique to be aware of, and are particularly useful when working with union types.
365
361
366
362
However, there are situations where they aren't as type-safe as they may appear.
@@ -377,44 +373,67 @@ In this case, any object passed to `isAlbum` will be considered an `Album`, even
377
373
378
374
## Assertion Functions
379
375
380
-
Assertion functions are similar to type predicates, but they use the `asserts` keyword instead of `is`. They are used to assert that a condition is true and to narrow the type of a variable, and will throw an error if the condition is false.
376
+
Assertion functions look similar to type predicates, but they're used slightly differently. Instead of returning a boolean to indicate whether a value is of a certain type, assertion functions throw an error if the value isn't of the expected type.
381
377
382
378
Here's how we could rework the `isAlbum` type predicate to be an `assertIsItem` assertion function:
383
379
384
380
```typescript
385
381
function assertIsAlbum(input:unknown):assertsinputisAlbum {
386
-
if (!isAlbum(input)) {
382
+
if (
383
+
typeofinput==="object"&&
384
+
input!==null&&
385
+
"id"ininput&&
386
+
"title"ininput&&
387
+
"artist"ininput&&
388
+
"year"ininput
389
+
) {
387
390
thrownewError("Not an Album!");
388
391
}
389
392
}
390
393
```
391
394
392
-
The `assertIsAlbum` function takes in a `input` of type `unknown` and asserts that it is an `Album` using the `asserts input is Album` syntax. If the `isAlbum` function returns `false`, an error is thrown.
395
+
The `assertIsAlbum` function takes in a `input` of type `unknown` and asserts that it is an `Album` using the `asserts input is Album` syntax.
393
396
394
-
Narrowing with assertion functions also works similarly to type predicates, with a notable difference:
397
+
This means that the narrowing is more aggressive. Instead of checking within an `if` statement, the function call itself is enough to assert that the `input` is an `Album`.
395
398
396
399
```typescript
397
400
function getAlbumTitle(item:unknown) {
401
+
// 'item' is unknown here
402
+
398
403
assertIsAlbum(item);
399
-
// At this point, 'item' is of type 'Album'
404
+
405
+
// After the assertion, 'item' is narrowed to 'Album'
400
406
console.log(item.title);
401
407
}
402
408
```
403
409
404
-
There's no need to use a conditional statement when calling `assertIsAlbum` because the function will throw an error if the `item` isn't an `Album`. If the function doesn't throw an error, we can access the `item`'s properties without any type errors.
410
+
Assertion functions can be useful when you want to ensure that a value is of a certain type before proceeding with further operations.
411
+
412
+
### Assertion Functions Can Lie
405
413
406
-
However, it's important to note that TypeScript won't alert you of the an error until the code is compiled.
414
+
Just like type predicates, assertion functions can be misused. If the assertion function doesn't accurately reflect the type being checked, it can lead to runtime errors.
407
415
408
-
Creating a variable by calling `getAlbumTitle` with a string will not raise an error until the code is compiled:
416
+
For example, if the `assertIsAlbum` function doesn't check for all the required properties of an `Album`, it can lead to unexpected behavior:
409
417
410
418
```typescript
411
-
let title =getAlbumTitle("Some string"); // no error in VS Code
419
+
function assertIsAlbum(input:unknown):assertsinputisAlbum {
420
+
if (typeofinput==="object") {
421
+
thrownewError("Not an Album!");
422
+
}
423
+
}
424
+
425
+
let item =null;
426
+
427
+
assertIsAlbum(item);
412
428
413
-
// during compilation, TypeScript will raise an error:
414
-
Error: NotanAlbum!
429
+
// 'item' is now narrowed to 'Album', even though it's
430
+
// null at runtime
431
+
item.title;
415
432
```
416
433
417
-
Assertion functions are particularly useful when you want to validate the type of a variable and throw an error if the validation fails. As with type predicates, it's important to ensure that the assertion function accurately reflects the type being checked in order to avoid any potential runtime issues.
434
+
In this case, the `assertIsAlbum` function doesn't check for the required properties of an `Album` - it just checks if `typeof input` is `"object"`. This means we've left ourselves open to a stray `null`. The famous JavaScript quirk where `typeof null === 'object'` will cause a runtime error when we try to access the `title` property.
0 commit comments