Skip to content

Commit 4f207f0

Browse files
committed
Continued working
1 parent 419e47b commit 4f207f0

File tree

1 file changed

+67
-36
lines changed

1 file changed

+67
-36
lines changed

book-content/chapters/16-the-utils-folder.md

Lines changed: 67 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -433,8 +433,6 @@ item.title;
433433

434434
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.
435435

436-
<!-- CONTINUE -->
437-
438436
## Function Overloads
439437

440438
Function overloads provide a way to define multiple function signatures for a single function implementation. In other words, you can define different ways to call a function, each with its own set of parameters and return types. It's an interesting technique for creating a flexible API that can handle different use cases while maintaining type safety.
@@ -443,22 +441,26 @@ To demonstrate how function overloads work, we'll create a `searchMusic` functio
443441

444442
### Defining Overloads
445443

446-
To define function overloads, the same function definition is written multiple times with different parameter and return types. Each definition is called an overload signature, and is separated by semicolons.
444+
To define function overloads, the same function definition is written multiple times with different parameter and return types. Each definition is called an overload signature, and is separated by semicolons. You'll also need to use the `function` keyword each time.
447445

448-
For the `searchMusic` example, we want to allow users to search by providing a string with keywords that will match songs, albums, or artists. We also want to allow for an advanced search by accepting an object with properties for `artist`, `genre`, and `year`.
446+
For the `searchMusic` example, we want to allow users to search by providing an artist, genre and year. But for legacy reasons, we want them to be able to pass them as a single object or as separate arguments.
449447

450-
Here's how we could define these function overload signatures:
448+
Here's how we could define these function overload signatures. The first signature takes in three separate arguments, while the second signature takes in a single object with the properties:
451449

452450
```typescript
453-
function searchMusic(query: string): void;
451+
function searchMusic(artist: string, genre: string, year: number): void;
454452
function searchMusic(criteria: {
455-
artist?: string;
456-
genre?: string;
457-
year?: number;
453+
// red squiggly line under searchMusic
454+
artist: string;
455+
genre: string;
456+
year: number;
458457
}): void;
458+
459+
// Hovering over searchMusic shows:
460+
// Function implementation is missing or not immediately following the declaration.
459461
```
460462

461-
With the overloads in place, we can now define the implementation signature.
463+
But we're getting an error. We've declared some ways this function should be declared, but we haven't provided the implementation yet.
462464

463465
### The Implementation Signature
464466

@@ -467,53 +469,82 @@ The implementation signature is the actual function declaration that contains th
467469
In this case, the implementation signature will take in a parameter called `queryOrCriteria` that can be either a `string` or an object with the specified properties. Inside the function, we'll check the type of `queryOrCriteria` and perform the appropriate search logic based on the provided arguments:
468470

469471
```typescript
470-
function searchMusic(query: string): void;
472+
function searchMusic(artist: string, genre: string, year: number): void;
471473
function searchMusic(criteria: {
472-
artist?: string;
473-
genre?: string;
474-
year?: number;
474+
artist: string;
475+
genre: string;
476+
year: number;
475477
}): void;
476478
function searchMusic(
477-
queryOrCriteria: string | { artist?: string; genre?: string; year?: number },
479+
artistOrCriteria: string | { artist: string; genre: string; year: number },
480+
genre?: string,
481+
year?: number,
478482
): void {
479-
if (typeof queryOrCriteria === "string") {
480-
console.log(`Searching for music with query: ${queryOrCriteria}`);
481-
// general search logic
483+
if (typeof artistOrCriteria === "string") {
484+
// Search with separate arguments
485+
search(artistOrCriteria, genre, year);
482486
} else {
483-
const { artist, genre, year } = queryOrCriteria;
484-
console.log(
485-
`Searching for music with criteria: ${JSON.stringify({
486-
artist,
487-
genre,
488-
year,
489-
})}`,
487+
// Search with object
488+
search(
489+
artistOrCriteria.artist,
490+
artistOrCriteria.genre,
491+
artistOrCriteria.year,
490492
);
491-
// specific search logic
492493
}
493494
}
494495
```
495496

496497
Now we can call the `searchMusic` function with the different arguments defined in the overloads:
497498

498499
```typescript
499-
searchMusic("King Gizzard and the Lizard Wizard"); // Valid
500-
searchMusic({ genre: "Psychedelic Rock" }); // Valid
500+
searchMusic("King Gizzard and the Lizard Wizard", "Psychedelic Rock", 2021);
501+
searchMusic({
502+
artist: "Tame Impala",
503+
genre: "Psychedelic Rock",
504+
year: 2015,
505+
});
501506
```
502507

503508
However, TypeScript will warn us if we attempt to pass in an argument that doesn't match any of the defined overloads:
504509

505510
```typescript
506-
searchMusic(1337); // red squiggly line under 1337
511+
searchMusic(
512+
// red squiggly line under searchMusic
513+
{
514+
artist: "Tame Impala",
515+
genre: "Psychedelic Rock",
516+
year: 2015,
517+
},
518+
"Psychedelic Rock",
519+
);
507520

508-
// hovering over 1337 shows:
509-
No overload matches this call.
510-
Overload 1 of 2, '(query: string): void', gave the following error.
511-
Argument of type 'number' is not assignable to parameter of type 'string'.
512-
Overload 2 of 2, '(criteria: { artist?: string | undefined; genre?: string | undefined; year?: number | undefined; }): void', gave the following error.
513-
Type '1337' has no properties in common with type '{ artist?: string | undefined; genre?: string | undefined; year?: number | undefined; }'.
521+
// Hovering over searchMusic shows:
522+
// No overload expects 2 arguments, but overloads do exist that expect either 1 or 3 arguments.
514523
```
515524

516-
While there aren't too many use cases for function overloads in typical application development, it can be useful for adding support for multiple arguments to your utilities and libraries. However, you'll want to use them sparingly since too many overloads can make a function difficult to understand and maintain. If you get to a point where a function has too many overloads, look into refactoring it into more focused functions or using other patterns like optional parameters or union types.
525+
This error shows us that we're trying to call `searchMusic` with two arguments, but the overloads only expect one or three arguments.
526+
527+
### Function Overloads vs Unions
528+
529+
<!-- CONTINUE -->
530+
531+
Function overloads can be useful when you want to express multiple ways to call a function that spread out over different parameters. In the example above, we can either call the function with separate arguments or with a single object.
532+
533+
When you have the same number of arguments but different types, you can use a union type instead of function overloads. For example, if you want to allow the user to search by either artist name or criteria object, you could use a union type:
534+
535+
```typescript
536+
function searchMusic(
537+
query: string | { artist: string; genre: string; year: number },
538+
): void {
539+
if (typeof query === "string") {
540+
// Search by artist
541+
searchByArtist(query);
542+
} else {
543+
// Search by genre
544+
searchByGenre(query.genre);
545+
}
546+
}
547+
```
517548

518549
## Exercises
519550

0 commit comments

Comments
 (0)