Skip to content

Commit

Permalink
deploy: 6a1de79
Browse files Browse the repository at this point in the history
  • Loading branch information
Workflow committed Jan 30, 2024
1 parent 88503dd commit b2f46c7
Show file tree
Hide file tree
Showing 31 changed files with 99 additions and 99 deletions.
2 changes: 1 addition & 1 deletion advanced-typescript.html
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,7 @@ <h2>Outline</h2>
</ul></li>
</ul>
<h2>Next Steps</h2>
<p>✏️ Head over to the <a href="advanced-typescript/typing-systems.html">first lesson</a> and strengthen the way you think about Typescript's typing systems.</p>
<p>✏️ Head over to the <a href="advanced-typescript/typing-systems.html">first lesson</a> and strengthen the way you think about TypeScript’s typing systems.</p>

</section>

Expand Down
20 changes: 10 additions & 10 deletions advanced-typescript/conditional-types.html
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,7 @@ <h2>Conditional Types</h2>
const wild = createBattle(&quot;wild-pokemon-battle&quot;); // Type is WildPokemonBattle
</code></pre>
<div line-highlight='1,3-5,7-8,'></div>
<p>The magic here happens in the <code>Battle</code> conditional type. In <code>Battle</code> we look for whichever <code>BattleType</code> is passed in. If it's <code>&quot;wild-pokemon-battle&quot;</code> we resolve to <code>WildPokemonBattle</code>, otherwise we resolve to <code>TrainerBattle</code>. Another cool feature of conditional types is that they are nestable. To demonstrate this, let’s take this battle situation a step further and make a couple of adjustments. Let’s add a third battle type.</p>
<p>The magic here happens in the <code>Battle</code> conditional type. In <code>Battle</code> we look for whichever <code>BattleType</code> is passed in. If its <code>&quot;wild-pokemon-battle&quot;</code> we resolve to <code>WildPokemonBattle</code>, otherwise we resolve to <code>TrainerBattle</code>. Another cool feature of conditional types is that they are nestable. To demonstrate this, let’s take this battle situation a step further and make a couple of adjustments. Let’s add a third battle type.</p>
<pre><code class="language-ts">type TrainerBattle = {
challenger: string;
};
Expand Down Expand Up @@ -566,7 +566,7 @@ <h2>Conditional Types With Unions</h2>
*/
type NonNullable&lt;T&gt; = T extends null | undefined ? never : T;
</code></pre>
<p>The definition may look complicated, but as we did with mapped types, let’s remove some of the noise and break it down bit by bit to see how this works. Let's start by placing the union into <code>NonNullable</code> directly.</p>
<p>The definition may look complicated, but as we did with mapped types, let’s remove some of the noise and break it down bit by bit to see how this works. Lets start by placing the union into <code>NonNullable</code> directly.</p>
<pre><code class="language-ts">type StringOrNumber = NonNullable&lt;string | number | undefined | null&gt;;
</code></pre>
<div line-highlight='1'></div>
Expand Down Expand Up @@ -597,7 +597,7 @@ <h2>Conditional Types With Unions</h2>
<p>The refinement aspects of conditional types become even stronger when used in conjunction with other TypeScript features we’ve seen so far. Let’s do a little more tweaking to our types from the <code>createBattle</code> example above and define a function called <code>emitBattleStart</code> that can take a battle name and a payload that corresponds to the properties in the named typed.</p>
<pre><code class="language-ts">emitBattleStart(&quot;wild-pokemon-battle&quot;, { challengingPokemon: &quot;onix&quot; });
</code></pre>
<p>Before we start looking at the <code>emitBattle</code> function let's make our types a little more functional. Instead of having <code>BattleTypes</code> separated from the rest of the battle types, we could use them as literals on our battle types to create a discriminating union.</p>
<p>Before we start looking at the <code>emitBattle</code> function lets make our types a little more functional. Instead of having <code>BattleTypes</code> separated from the rest of the battle types, we could use them as literals on our battle types to create a discriminating union.</p>
<pre><code class="language-ts">type TrainerBattle = {
battleType: &quot;trainer-battle&quot;;
challenger: string;
Expand Down Expand Up @@ -642,7 +642,7 @@ <h2>Conditional Types With Unions</h2>
}
</code></pre>
<p>The next part is where it gets tricky. Somehow we need to get the correct object shape for the battle information. We can break it down like this. First, we need to select the member of the <code>PokemonBattles</code> union that corresponds to the <code>BattleType</code> from the first parameter of the function. Second, we need to remove the <code>battleType</code> key from that type. And finally, we need to map over the new keys, making sure they have the same values.</p>
<p>Starting with the first step, let's find a way to select the correct member of the <code>PokemonBattles</code> union. We can accomplish this with our good friend conditional types. We can use generics to give our <code>BattleInformation</code> type access to the <code>PokemonBattles</code> union and the <code>BattleType</code> and inside it uses a conditional type to refine the union to being the battle type we’re after.</p>
<p>Starting with the first step, lets find a way to select the correct member of the <code>PokemonBattles</code> union. We can accomplish this with our good friend conditional types. We can use generics to give our <code>BattleInformation</code> type access to the <code>PokemonBattles</code> union and the <code>BattleType</code> and inside it uses a conditional type to refine the union to being the battle type we’re after.</p>
<pre><code class="language-ts">type BattleInformation&lt;BattleType, Battle&gt; = Battle extends {
battleType: BattleType;
}
Expand All @@ -664,7 +664,7 @@ <h2>Conditional Types With Unions</h2>
<p>Next, we need a way to get rid of the <code>battleType</code> key from this new type. Luckily for us, TypeScript provides a utility type that does just that – <code>Exclude</code>. Exclude takes two unions and <strong>excludes</strong> the members of the second union from the first. As you might have guessed, <code>Exclude</code> uses conditional types under the hood to provide this functionality. For our case, we want to exclude <code>battleType</code> from the keys of the battle.</p>
<pre><code class="language-ts">Exclude&lt;keyof Battle, &quot;battleType&quot;&gt;;
</code></pre>
<p>Our final step is to map over these and reconstruct the type. To do this, let's define a <code>FormatBattle</code> type that does this for us and pass our refined <code>Battle</code> type into it.</p>
<p>Our final step is to map over these and reconstruct the type. To do this, lets define a <code>FormatBattle</code> type that does this for us and pass our refined <code>Battle</code> type into it.</p>
<pre><code class="language-ts">type FormatBattle&lt;Battle&gt; = {
[FilteredKey in Exclude&lt;keyof Battle, &quot;battleType&quot;&gt;]: Battle[FilteredKey];
};
Expand Down Expand Up @@ -693,7 +693,7 @@ <h2>Conditional Types With Unions</h2>
)
</code></pre>
<div line-highlight='3, 7-13'></div>
<p>That said, we can take it a little bit further and add an alias and default to help make the typing a bit more semantic. It feels a little weird needing to pass in <code>PokemonBattles</code>, and the functionality of the type and how it's used seem to differ a bit. One extra type we can resolve this. Let’s update <code>BattleInformation</code> to alias our conditional mapping and give a more semantically relevant name like <code>GetBattleInformation</code>.</p>
<p>That said, we can take it a little bit further and add an alias and default to help make the typing a bit more semantic. It feels a little weird needing to pass in <code>PokemonBattles</code>, and the functionality of the type and how its used seem to differ a bit. One extra type we can resolve this. Let’s update <code>BattleInformation</code> to alias our conditional mapping and give a more semantically relevant name like <code>GetBattleInformation</code>.</p>
<pre><code class="language-ts">type FormatBattle&lt;Battle&gt; = {
[FilterKey in Exclude&lt;keyof Battle, &quot;battleType&quot;&gt;]: Battle[FilterKey];
};
Expand Down Expand Up @@ -748,7 +748,7 @@ <h2>A New and Powerful Syntax</h2>
<h2>Exercises</h2>
<h3>Exercise 1</h3>
<p>We used exclude in one of the examples in the content of this section.
Let's take a moment to create the type ourselves. Exlude takes two generics
Lets take a moment to create the type ourselves. Exlude takes two generics
<code>T</code> and <code>U</code> and removes the memebers in <code>U</code> from <code>T</code>.</p>
<pre><code class="language-ts">type WildPokemonBattle = {
battleType: &quot;wild-pokemon-battle&quot;;
Expand All @@ -765,7 +765,7 @@ <h3>Exercise 1</h3>
* Exercise 1
*
* We used exclude in one of the examples in the content of this section.
* Let's take a moment to create the type ourselves. Exlude takes two generics
* Lets take a moment to create the type ourselves. Exlude takes two generics
* `T` and `U` and removes the memebers in `U` from `T`.
*
* ```ts
Expand All @@ -788,7 +788,7 @@ <h3>Exercise 1</h3>
<div line-highlight='1'></div>
<p></details></p>
<h3>Exercise 2</h3>
<p>Let's build out an example similar to <code>ArrayElement</code> provided by the TypeScript documentation (No cheating by looking it up!)</p>
<p>Lets build out an example similar to <code>ArrayElement</code> provided by the TypeScript documentation (No cheating by looking it up!)</p>
<p><code>Flatten</code> should a new type that unnests arrays by one level.</p>
<pre><code class="language-ts">type FlattenedStringArray = Flatten&lt;string[]&gt;; // string
type FlatString = Flatten&lt;string&gt;; // string
Expand All @@ -799,7 +799,7 @@ <h3>Exercise 2</h3>
<pre><code class="language-ts">/**
Exercise 2
*
* Let's build out an example similar to `ArrayElement` provided by the TypeScript documentation (No cheating by looking it up!)
* Lets build out an example similar to `ArrayElement` provided by the TypeScript documentation (No cheating by looking it up!)
*
* `Flatten` should create a new type that unnests arrays by one level.
*
Expand Down
6 changes: 3 additions & 3 deletions advanced-typescript/generics-with-constraints.html
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ <h2>Quick Recap on Generics</h2>
</code></pre>
<p>Generics are a great way to reuse strongly typed code and create powerful abstractions in your libraries and applications.</p>
<h2>With Constraints</h2>
<p>Generics by themselves are a great way to encapsulate logic when we don’t really care what shape that type has. Take for example <code>Array</code>. Arrays are a list of things. It doesn’t matter what things are in it since when we do interact with items in an array we provide the logic to handle those items, making the array indifferent to what’s inside. Sometimes though, we do care about the shape of the thing being passed in or at least part of it. Take a <code>greeter</code> function, say we want it to greet something by name whether it be a bird, dog, person, cat, or pokemon. Reuse of logic across types screams generics. So what happens if we try to implement <code>greeter</code> using plain generics? Well, let's try.</p>
<p>Generics by themselves are a great way to encapsulate logic when we don’t really care what shape that type has. Take for example <code>Array</code>. Arrays are a list of things. It doesn’t matter what things are in it since when we do interact with items in an array we provide the logic to handle those items, making the array indifferent to what’s inside. Sometimes though, we do care about the shape of the thing being passed in or at least part of it. Take a <code>greeter</code> function, say we want it to greet something by name whether it be a bird, dog, person, cat, or pokemon. Reuse of logic across types screams generics. So what happens if we try to implement <code>greeter</code> using plain generics? Well, lets try.</p>
<pre><code class="language-ts">function greeter&lt;T&gt;(thingToGreet: T): void {
// Error: Property 'name' does not exist on type 'T'.
console.log(&quot;Hello &quot; + thingToGreet.name);
Expand Down Expand Up @@ -636,7 +636,7 @@ <h3>Exercise 1</h3>
<div line-highlight='1'></div>
<p></details></p>
<h3>Exercise 2</h3>
<p>Now that we have a <code>Keys</code> type let's put it to work. Imagine we have the following types:</p>
<p>Now that we have a <code>Keys</code> type lets put it to work. Imagine we have the following types:</p>
<pre><code class="language-ts">type PokedexEntry = string;

type LeafStarterPokemon = {
Expand Down Expand Up @@ -694,7 +694,7 @@ <h3>Exercise 2</h3>

/* Exercise 2
*
* Now that we have a `Keys` type let's put it to work. Below is a function called `getStarterPokemonInfomation` that
* Now that we have a `Keys` type lets put it to work. Below is a function called `getStarterPokemonInfomation` that
* takes two generics. We would like to be able to pass in any of our three starters objects and a starter’s name to get the
* data for that pokemon. Update the generics definition in the function to allow for this to happen.
*
Expand Down
Loading

0 comments on commit b2f46c7

Please sign in to comment.