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.
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 number
s) 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.
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:
- The New TypeScript Handbook
- The TypeScript Community Discord
- The comments on each Dev.to post!
- Our previous
Type | Treat
2020 challenges
Happy Typing 🙂
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: