Skip to content

Commit 7675fa9

Browse files
committed
Added exercises to everything
1 parent 68cb1f5 commit 7675fa9

11 files changed

+321
-2
lines changed

book-content/chapters/02-ide-superpowers.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -576,4 +576,4 @@ function getRandomPercentage() {
576576
}
577577
```
578578

579-
These are just some of the options provided by the Quick Fix menu. There's so much you can achieve with them, and we're only scratching the surface. Keep exploring and experimenting to discover their full potential!
579+
These are just some of the options provided by the Quick Fix menu. There's so much you can achieve with them, and we're only scratching the surface. Keep exploring and experimenting to discover their full potential!

book-content/chapters/05-unions-literals-and-narrowing.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1462,6 +1462,11 @@ However, TypeScript is showing us errors below `'radius'` and `'sideLength'`.
14621462

14631463
Your task is to update the implementation of the `calculateArea` function so that destructuring properties from the passed in `Shape` works without errors. Hint: the examples I showed in the chapter _didn't_ use destructuring, but some destructuring is possible.
14641464

1465+
<Exercise
1466+
title="Exercise 1: Destructuring a Discriminated Union"
1467+
filePath="/src/018-unions-and-narrowing/075-destructuring-a-discriminated-union.problem.ts"
1468+
/>
1469+
14651470
#### Exercise 2: Narrowing a Discriminated Union with a Switch Statement
14661471

14671472
Here we have our `calculateArea` function from the previous exercise, but without any destructuring.
@@ -1478,6 +1483,11 @@ function calculateArea(shape: Shape) {
14781483

14791484
Your challenge is to refactor this function to use a `switch` statement instead of the `if/else` statement. The `switch` statement should be used to narrow the type of `shape` and calculate the area accordingly.
14801485

1486+
<Exercise
1487+
title="Exercise 2: Narrowing a Discriminated Union with a Switch Statement"
1488+
filePath="/src/018-unions-and-narrowing/076-narrowing-a-discriminated-union-with-a-switch-statement.problem.ts"
1489+
/>
1490+
14811491
#### Exercise 3: Discriminated Tuples
14821492

14831493
Here we have a `fetchData` function that returns a promise that resolves to an `APIResponse` tuple that consists of two elements.
@@ -1570,6 +1580,11 @@ If the first element is `"success"`, then the second element should be an array
15701580

15711581
Your challenge is to redefine the `APIResponse` type to be a discriminated tuple that only allows for the specific combinations for the `success` and `error` states defined above.
15721582

1583+
<Exercise
1584+
title="Exercise 3: Discriminated Tuples"
1585+
filePath="/src/018-unions-and-narrowing/078-destructuring-a-discriminated-tuple.problem.ts"
1586+
/>
1587+
15731588
#### Exercise 4: Handling Defaults with a Discriminated Union
15741589

15751590
We're back with our `calculateArea` function:
@@ -1634,6 +1649,11 @@ Your challenge is to:
16341649
1. Make updates to the `Shape` discriminated union that will allow for us to omit `kind`.
16351650
2. Make adjustments to the `calculateArea` function to ensure that TypeScript's type narrowing works properly within the function.
16361651

1652+
<Exercise
1653+
title="Exercise 4: Handling Defaults with a Discriminated Union"
1654+
filePath="/src/018-unions-and-narrowing/080-adding-defaults-to-discriminated-union.problem.ts"
1655+
/>
1656+
16371657
#### Solution 1: Destructuring a Discriminated Union
16381658

16391659
Before we look at the working solution, let's look at an attempt that doesn't work out.

book-content/chapters/06-objects.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,12 +323,22 @@ type Product = {
323323

324324
Your task is to create a new `BaseEntity` type that includes the `id` and `createdAt` properties. Then, use the `&` operator to create `User` and `Product` types that intersect with `BaseEntity`.
325325

326+
<Exercise
327+
title="Exercise 1: Create an Intersection Type"
328+
filePath="/src/020-objects/081-extend-object-using-intersections.problem.ts"
329+
/>
330+
326331
#### Exercise 2: Extending Interfaces
327332

328333
After the previous exercise, you'll have a `BaseEntity` type along with `User` and `Product` types that intersect with it.
329334

330335
This time, your task is to refactor the types to be interfaces, and use the `extends` keyword to extend the `BaseEntity` type. For extra credit, try creating and extending multiple smaller interfaces.
331336

337+
<Exercise
338+
title="Exercise 2: Extending Interfaces"
339+
filePath="/src/020-objects/082-extend-object-using-interfaces.problem.ts"
340+
/>
341+
332342
#### Solution 1: Create an Intersection Type
333343

334344
To solve this challenge, we'll create a new `BaseEntity` type with the common properties:
@@ -668,6 +678,11 @@ scores.science = 85;
668678

669679
Your task is to give `scores` a type annotation to support the dynamic subject keys. There are three ways: an inline index signature, a type, an interface, or a `Record`.
670680

681+
<Exercise
682+
title="Exercise 1: Use an Index Signature for Dynamic Keys"
683+
filePath="/src/020-objects/084-index-signatures.problem.ts"
684+
/>
685+
671686
#### Exercise 2: Default Properties with Dynamic Keys
672687

673688
Here, we're trying to model a situation where we want some required keys - `math`, `english`, and `science` - on our scores object.
@@ -693,6 +708,11 @@ The definition of scores should be erroring, because `science` is missing - but
693708

694709
Your task is to update the `Scores` interface to specify default keys for `math`, `english`, and `science` while allowing for any other subject to be added. Once you've updated the type correctly, the red squiggly line below `@ts-expect-error` will go away because `science` will be required but missing. See if you can use `interface extends` to achieve this.
695710

711+
<Exercise
712+
title="Exercise 2: Default Properties with Dynamic Keys"
713+
filePath="/src/020-objects/085-index-signatures-with-defined-keys.problem.ts"
714+
/>
715+
696716
#### Exercise 3: Restricting Object Keys With Records
697717

698718
Here we have a `configurations` object, typed as `Configurations` which is currently unknown.
@@ -730,6 +750,11 @@ const configurations: Configurations = {
730750

731751
Update the `Configurations` type so that only the keys from `Environment` are allowed on the `configurations` object. Once you've updated the type correctly, the red squiggly line below `@ts-expect-error` will go away because `notAllowed` will be disallowed properly.
732752

753+
<Exercise
754+
title="Exercise 3: Restricting Object Keys With Records"
755+
filePath="/src/020-objects/087-record-type-with-union-as-keys.problem.ts"
756+
/>
757+
733758
#### Exercise 4: Dynamic Key Support
734759

735760
Consider this `hasKey` function that accepts an object and a key, then calls `object.hasOwnProperty` on that object:
@@ -802,6 +827,11 @@ it("Should work on symbol keys", () => {
802827

803828
Your task is to update the `hasKey` function so that all of these tests pass. Try to be as concise as possible!
804829

830+
<Exercise
831+
title="Exercise 4: Dynamic Key Support"
832+
filePath="/src/020-objects/086-property-key-type.problem.ts"
833+
/>
834+
805835
#### Solution 1: Use an Index Signature for Dynamic Keys
806836

807837
Here are the three solutions:
@@ -1262,6 +1292,11 @@ Your task is to update the typing so that only the `name` and `email` fields are
12621292

12631293
You can use the helper types we've looked at to accomplish this, but for extra practice try using just interfaces.
12641294

1295+
<Exercise
1296+
title="Exercise 1: Expecting Certain Properties"
1297+
filePath="/src/020-objects/089-pick-type-helper.problem.ts"
1298+
/>
1299+
12651300
#### Exercise 2: Updating a Product
12661301

12671302
Here we have a function `updateProduct` that takes two arguments: an `id`, and a `productInfo` object derived from the `Product` type, excluding the `id` field.
@@ -1308,6 +1343,11 @@ updateProduct(1, {
13081343

13091344
Your challenge is to modify the `productInfo` parameter to reflect these requirements. The `id` should remain absent from `productInfo`, but we also want all other properties in `productInfo` to be optional.
13101345

1346+
<Exercise
1347+
title="Exercise 2: Updating a Product"
1348+
filePath="/src/020-objects/091-omit-type-helper.problem.ts"
1349+
/>
1350+
13111351
#### Solution 1: Expecting Certain Properties
13121352

13131353
There are quite a few ways to solve this problem. Here are a few examples:

book-content/chapters/07-mutability.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,11 @@ modifyButtons(buttonsToChange);
373373

374374
Your task is to determine why this error shows up, then resolve it.
375375

376+
<Exercise
377+
title="Exercise 1: Inference with an Array of Objects"
378+
filePath="/src/028-mutability/098-object-property-inference.problem.ts"
379+
/>
380+
376381
#### Exercise 2: Avoiding Array Mutation
377382

378383
This `printNames` function accepts an array of `name` strings and logs them to the console. However, there are also non-working `@ts-expect-error` comments that should not allow for names to be added or changed:
@@ -394,6 +399,11 @@ function printNames(names: string[]) {
394399

395400
Your task is to update the type of the `names` parameter so that the array cannot be mutated. There are two ways to solve this problem.
396401

402+
<Exercise
403+
title="Exercise 2: Avoiding Array Mutation"
404+
filePath="/src/028-mutability/103-readonly-arrays.problem.ts"
405+
/>
406+
397407
#### Exercise 3: An Unsafe Tuple
398408

399409
Here we have a `dangerousFunction` which accepts an array of numbers as an argument:
@@ -439,6 +449,11 @@ Your task is to adjust the type of `Coordinate` such that TypeScript triggers an
439449

440450
Note that you should only change `Coordinate`, and leave the function untouched.
441451

452+
<Exercise
453+
title="Exercise 3: An Unsafe Tuple"
454+
filePath="/src/028-mutability/104.5-fixing-unsafe-tuples.problem.ts"
455+
/>
456+
442457
#### Solution 1: Inference with an Array of Objects
443458

444459
Hovering over the `buttonsToChange` variable shows us that it is being inferred as an array of objects with a `type` property of type `string`:
@@ -791,6 +806,11 @@ Depending on whether or not the fetch operation is successful, the tuple should
791806

792807
Hint: There are two possible approaches to solve this challenge. One way would be to define an explicit return type for the function. Alternatively, you could attempt to add or change type annotations for the `return` values within the function.
793808

809+
<Exercise
810+
title="Exercise 1: Returning A Tuple From A Function"
811+
filePath="/src/028-mutability/106-as-const-to-make-functions-infer-a-tuple.problem.ts"
812+
/>
813+
794814
#### Exercise 2: Inferring Literal Values In Arrays
795815

796816
Let's revisit a previous exercise and evolve our solution.
@@ -834,6 +854,11 @@ This time, your challenge is to solve the error by finding a different solution.
834854

835855
You should not alter the `ButtonAttributes` type definition or the `modifyButtons` function.
836856

857+
<Exercise
858+
title="Exercise 2: Inferring Literal Values In Arrays"
859+
filePath="/src/028-mutability/107-as-const-can-make-strings-infer-as-their-literals-in-objects.explainer.ts"
860+
/>
861+
837862
#### Solution 1: Returning A Tuple From A Function
838863

839864
As mentioned, there are two different solutions to this challenge.

book-content/chapters/08-classes.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,11 @@ As seen from the `@ts-expect-error` directives, we also expect these properties
723723

724724
Your challenge is to implement the `CanvasNode` class to satisfy these requirements. For extra practice, solve the challenge with and without the use of a constructor.
725725

726+
<Exercise
727+
title="Exercise 1: Creating a Class"
728+
filePath="/src/030-classes/108-understand-classes.problem.ts"
729+
/>
730+
726731
### Exercise 2: Implementing Class Methods
727732

728733
In this exercise, we've simplified our `CanvasNode` class so that it no longer has read-only properties:
@@ -761,6 +766,11 @@ Currently, there is an error under the `move` method call because the `CanvasNod
761766

762767
Your task is to add a `move` method to the `CanvasNode` class that will update the `x` and `y` properties to the new location.
763768

769+
<Exercise
770+
title="Exercise 2: Implementing Class Methods"
771+
filePath="/src/030-classes/109-class-methods.problem.ts"
772+
/>
773+
764774
### Exercise 3: Implement a Getter
765775

766776
Let's continue working with the `CanvasNode` class, which now has a constructor that accepts an optional argument, renamed to `position`. This `position` is an object that replaces the individual `x` and `y` we had before:
@@ -826,6 +836,11 @@ it("Should be able to receive an initial position", () => {
826836

827837
Your task is to update the `CanvasNode` class to include a `position` getter that will allow for the test cases to pass.
828838

839+
<Exercise
840+
title="Exercise 3: Implement a Getter"
841+
filePath="/src/030-classes/111-getters.problem.ts"
842+
/>
843+
829844
### Exercise 4: Implement a Setter
830845

831846
The `CanvasNode` class has been updated so that `x` and `y` are now private properties:
@@ -860,6 +875,11 @@ canvasNode.position = { x: 10, y: 20 };
860875

861876
Your task is to write a setter for the `position` property that will allow for the test case to pass.
862877

878+
<Exercise
879+
title="Exercise 4: Implement a Setter"
880+
filePath="/src/030-classes/113-setters.problem.ts"
881+
/>
882+
863883
### Exercise 5: Extending a Class
864884

865885
Here we have a more complex version of the `CanvasNode` class.
@@ -889,6 +909,11 @@ Your task is to refactor the `CanvasNode` class to split the `x` and `y` propert
889909
890910
If you like, you can use an `abstract` class to define `Shape`.
891911
912+
<Exercise
913+
title="Exercise 5: Extending a Class"
914+
filePath="/src/030-classes/114-extending-other-classes.problem.ts"
915+
/>
916+
892917
### Solution 1: Creating a Class
893918
894919
Here's an example of a `CanvasNode` class with a constructor that meets the requirements:

book-content/chapters/10-deriving-types.md

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,12 @@ Notice there is a lot of duplication here. Both the `FormValues` interface and `
522522

523523
Your task is to modify the `inputs` Record so its keys are derived from the `FormValues` interface.
524524

525+
526+
<Exercise
527+
title="Exercise 1: Reduce Key Repetition"
528+
filePath="/src/040-deriving-types-from-values/125-keyof.problem.ts"
529+
/>
530+
525531
### Exercise 2: Derive a Type from a Value
526532

527533
Here, we have an object named `configurations` that comprises a set of deployment environments for `development`, `production`, and `staging`.
@@ -555,6 +561,12 @@ We want to use the `Environment` type across our application. However, the `conf
555561

556562
Your task is to update the `Environment` type so that it is derived from the `configurations` object.
557563

564+
565+
<Exercise
566+
title="Exercise 2: Derive a Type from a Value"
567+
filePath="/src/040-deriving-types-from-values/126-typeof-keyword.problem.ts"
568+
/>
569+
558570
### Exercise 3: Accessing Specific Values
559571

560572
Here were have an `programModeEnumMap` object that keeps different groupings in sync. There is also a `ProgramModeMap` type that uses `typeof` to represent the entire enum mapping:
@@ -585,6 +597,12 @@ type test = Expect<Equal<Group, "group">>;
585597

586598
Your task is to find the proper way to type `Group` so the test passes as expected.
587599

600+
601+
<Exercise
602+
title="Exercise 3: Accessing Specific Values"
603+
filePath="/src/040-deriving-types-from-values/135-indexed-access-types.problem.ts"
604+
/>
605+
588606
### Exercise 4: Unions with Indexed Access Types
589607

590608
This exercise starts with the same `programModeEnumMap` and `ProgramModeMap` as the previous exercise:
@@ -613,6 +631,12 @@ type test = Expect<
613631

614632
This time, your challenge is to update the `PlannedPrograms` type to use an indexed access type to extract a union of the `ProgramModeMap` values that included "`planned`".
615633

634+
635+
<Exercise
636+
title="Exercise 4: Unions with Indexed Access Types"
637+
filePath="/src/040-deriving-types-from-values/136-pass-unions-to-indexed-access-types.problem.ts"
638+
/>
639+
616640
### Exercise 5: Extract a Union of All Values
617641

618642
We're back with the `programModeEnumMap` and `ProgramModeMap` type:
@@ -652,6 +676,11 @@ type test = Expect<
652676

653677
Using what you've learned so far, your task is to update the `AllPrograms` type to use an indexed access type to create a union of all the values from the `programModeEnumMap` object.
654678

679+
<Exercise
680+
title="Exercise 5: Extract a Union of All Values"
681+
filePath="/src/040-deriving-types-from-values/137-pass-keyof-into-an-indexed-access-type.problem.ts"
682+
/>
683+
655684
### Exercise 6: Create a Union from an `as const` Array
656685

657686
Here's an array of `programModes` wrapped in an `as const`:
@@ -691,6 +720,11 @@ Your task is to determine how to create the `AllPrograms` type in order for the
691720

692721
Note that just using `keyof` and `typeof` in an approach similar to the previous exercise's solution won't quite work to solve this one! This is tricky to find - but as a hint: you can pass primitive types to indexed access types.
693722

723+
<Exercise
724+
title="Exercise 6: Create a Union from an `as const` Array"
725+
filePath="/src/040-deriving-types-from-values/138-create-a-union-from-an-as-const-array.problem.ts"
726+
/>
727+
694728
### Solution 1: Reduce Key Repetition
695729

696730
The solution is to use `keyof` to extract the keys from the `FormValues` interface and use them as the keys for the `inputs` Record:
@@ -1041,6 +1075,11 @@ In addition to being a bit annoying to write and read, the other problem with th
10411075
10421076
Your task is to use a utility type to fix this problem.
10431077
1078+
<Exercise
1079+
title="Exercise 7: A Single Source of Truth"
1080+
filePath="/src/040-deriving-types-from-values/132-parameters-type-helper.problem.ts"
1081+
/>
1082+
10441083
### Exercise 8: Typing Based on Return Value
10451084
10461085
Say we're working with a `createUser` function from a third-party library:
@@ -1079,6 +1118,11 @@ type test = Expect<
10791118
10801119
Your task is to update the `User` type so the test passes as expected.
10811120
1121+
<Exercise
1122+
title="Exercise 8: Typing Based on Return Value"
1123+
filePath="/src/040-deriving-types-from-values/133-return-type.problem.ts"
1124+
/>
1125+
10821126
### Exercise 9: Unwrapping a Promise
10831127
10841128
This time the `createUser` function from the third-party library is asynchronous:
@@ -1111,6 +1155,11 @@ Like before, assume that you do not have access to the implementation of the `fe
11111155
11121156
Your task is to update the `User` type so the test passes as expected.
11131157
1158+
<Exercise
1159+
title="Exercise 9: Unwrapping a Promise"
1160+
filePath="/src/040-deriving-types-from-values/134-awaited-type-helper.problem.ts"
1161+
/>
1162+
11141163
### Solution 7: A Single Source of Truth
11151164
11161165
The `Parameters` utility type is key to this solution, but there is an additional step to follow.
@@ -1442,4 +1491,4 @@ The decision to derive or decouple is all about reducing your future workload.
14421491
14431492
Are the two types so related that updates to one will need to ripple to the other? Derive.
14441493
1445-
Are they so unrelated that coupling them could result in more work down the line? Decouple.
1494+
Are they so unrelated that coupling them could result in more work down the line? Decouple.

0 commit comments

Comments
 (0)