Type | Treat – Day 5

Orta

Type | Treat Challenge 5

Welcome to the fifth, and last, Type | Treat challenge! These challenges are a series of blog posts which have 2 code challenges in, one for beginners and one for intermediate TypeScript programmers. We’re on day five, which means going over the answers from yesterday and have 2 new challenges.

Yesterday’s Solution

Beginner/Learner Challenge

This challenge aimed to be generics 101, first introducing the concept of making your function pass a type from the function to the argument:

-  function getBowl(items: any) {
+  function getBowl<T>(items: T) {
      return { items }
  }

The any acted as hint clue about where to look, and this example is almost the first code sample on the Generics chapter in the Handbook, so it felt like a good intro.

The second part involved understanding generic constraints, these are essential tools in helping you define the baselines for types which can be used in your function. In this case we didn’t provide the word “constraints” but opted for a more cryptic clue by setting up the function most of the way, then saying you only needed two words:

-  function fillBowl<T>(candy: T) {
+  function fillBowl<T extends string>(candy: T) {
      return { candy }
  }

By saying that T extended string then the string literals are correctly passed through the function – which removes all the compiler errors.

Our answer

Intermediate/Advanced Challenge

The intermediate challenge also invovled generic constraints, so if you had just finished the beginner’s then you were in a good place to figure this challenge. The key is to make a

- const check = (data: Competitor[]) => {
+ const check = <Type extends Competitor> (data: Type[]) => {
      return data.map(competitor => {
          if (competitor.weight > 2121.5) throw new Error("Stop the show, world record hit!")
          return { ...competitor, judge: (...args: unknown[]) => { } }
      })
  }

This is testing a few different things:

  • Writing generics with an arrow function
  • Using an extends constraint for the interface subtypes
  • Re-using the type parameter inside the array

We left a tricky problem with this challenge, but explicitly didn’t call it out. The function judge: (...args: unknown[]) is a types gap. There is no validating that the judge function actually works like expected. There are two approaches for handling this:

- return { ...competitor, judge: (...args: unknown[]) => { } }
+ return { ...competitor, judge: (...args: Array<T[keyof T]>) => { } }

This version from @faridz974 would ensure that the right values were used in the function (e.g. you couldn’t accidentally put in an object to something which could only accept string and numbers) but it ignored the order. An accurate, but whole-heartedly not recommended for production version which does take order into account comes from converting an interface to a tuple on GitHub which is a bit too long to print in here, but here’s a working implementation in the playground.

Our answer.

The Challenge

Beginner/Learner Challenge

Update your website’s color scheme for Halloween and tidy the codebase up a bit.

Intermediate/Advanced Challenge

Handle a book stores halloween discount event

How To Share Your Solution

Once you feel you have completed the challenge, you will need to select the Share button in the playground. This will automatically copy a playground URL to your clipboard.

Then either:

  • Go to Twitter, and create a tweet about the challenge, add the link to your code and mention the @TypeScript Twitter account with the hashtag #TypeOrTreat.

  • Leave us a comment with your feedback on the dev.to post, or in this post.

Best Resources for Additional Help

If you need additional help you can utilize the following:

Happy Typing 🙂

1 comment

Leave a comment

  • Alex Fung

    while academically challenging, why should we pass the individual members of competitor into judge(), and actually allows arguments of arbitrary values? Isn’t it more sensible to write:

    const check =  (data: T[]) => 
        data.map(competitor => {
            if (competitor.weight > 2121.5) throw new Error("Stop the show, world record hit!")
            return { ...competitor, judge: function () { console.log(this.weight) } }
        });