You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Welcome to TypeScript's first ever set of virtual code challenges: Type | Treat (or "Type or Treat")! We will be presenting some "spooky" code challenges that will allow you to get deeper into the TypeScript language but in a fun way.
Starting tomorrow, a new code challenge will be posted every weekday, along with its solution the day after. The last solution will be posted on Halloween day.
Are The Challenges For TypeScript Developers Only?
Absolutely not! We want all developers, familiar with TypeScript or not to be apart of Type | Treat. Every day will have two different types of challenges, one for beginners/learners and one for intermediate/advanced developers. That way everyone can participate.
Beginner/Learner Challenge
The TypeScript team is being hired to investigate recent hauntings in the community, and we are trying to figure which ghost is the trouble-maker!
We found an API that will allow us to get data on the ghosts however, out code isn't fully optimized. So we need your help.
Head to this link and help us figure out the best type to use in one of our function parameters!
Intermediate/Advanced Challenge
Your kids have come back from trick or treating with a lot of loot. Someone's going to have to sort this pile, and it looks like that job has fallen to you. Can you conditionally filter the pile into manageable lists?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
The solution for this challenge used indexed types to extract a part of an existing type to avoid duplication. The need to use [number] is an interesting twist, because [0] or [1] (or any number) would have worked just as well too. Doing this right would also have raised the typo in the original code.
// Your goal: remove this any, without changing GhostAPIResponse
- const displayHauntings = (haunting: any) => {+ const displayHauntings = (haunting: GhostAPIResponse["hauntings"][number]) => {
console.log(` - Title: ${haunting.title}`)
// You're first going to need to separate out the candy from the treats,
// you can do that via conditional types.
- // type AllCandies = ...+ type IsCandy<A> = A extends { candy: true } ? A : never;+ type AllCandies = IsCandy<ResultsFromHalloween>- // type AllTricks = ...+ type IsTrick<A> = A extends { trick: true } ? A : never;+ type AllTricks = IsTrick<ResultsFromHalloween>
// Almost there, but little 'Bobby Tables' cannot have peanuts. Can
// you make a list of candies just for him?
- // type AllCandiesWithoutPeanuts = ...
type HasPeanuts<A> = A extends { peanuts: true } ? A : never;
type AllCandiesWithoutPeanuts = HasPeanuts<AllCandies>
Our original answer relied on using Conditional Types to narrow the union, however we got a lot of responses using the Exclude utility type to make it a single liner:
Lets take a trip to the pumpkin patch and try to find the perfect one for our Jack O'Lantern. But in order to make sure we have located the right type of pumpkin, we need your help in identifying pumpkin types.
We created some starter code you can find here, lets see if you can finish it.
Intermediate/Advanced Challenge
Your job busting ghosts just got real tricky. Before you head in to guard Manhattan, you need to assert to those ghosts who is boss. Help finalize the ghost-busting algorithm because who else are you gonna call?
Need Extra Help?
If you need additional help you can utilize the following:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This one either stumped people for ten minutes or was considered a breeze. We had a few different answers in the TypeScript team, but we think this one is the most elegant.
Don't freak out... Ok freak out a little bit cause WE ARE BEING HAUNTED! And they are after our code! We have tried everything but we cant seem to get it right!
Somehow they keep manipulating the objects set this snippet of code. Take a look and see if you can force the ghosts to stop moving things around.
Intermediate/Advanced Challenge
You've been roped into helping out the halloween puppy parade, it was such a distracting even that you did the minimum possible to spend as much time watching the show.
Now it's over, you figure it's time to clean up your code and try get it type-safe.
Need Extra Help?
If you need additional help you can utilize the following:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This one either stumped people for ten minutes or was considered a breeze. We had a few different answers in the TypeScript team, but we think this one is the most elegant.
You've been keeping tally of how the houses on your street respond to trick-or-treaters. Can you reduce duplication from the types needed to describe the results?
You have a list of trunk or treat spots, in your rush you hardcoded the result of your map function to convert it from an array to an object.
Now the list is longer than three items, it's time to map that hardcoded result into a type. Can you refactor this TODO list function?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
We were looking for using the Readonly utility type to force the type to not allow poltergeists to make changes to your rooms.
- type Rectory = {+ type Rectory = Readonly<{
rooms: Room[]
noises: any[]
- }+ }>- type Room = {+ type Room = Readonly<{
name: string
doors: number
windows: number
ghost?: any
- }+ }>
It's worth remembering that JavaScript doesn't have immutability like this, so this really only affects the type system. You'll get a compiler error, but people can still work around that.
You're working on a horror movie night. You've skipped the types because you figured it'd be simple to work with but someone put the wrong movie in and The Nightmare Before Christmas is not a halloween movie. It's in the name. Anyway. To avoid this happening again, you figure it's time to add types to the function. Once, you got that down then you wonder if you can do the same thing for the kids schedule?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Like many challenges, you answer to this depends on how thorough you wanted to type the houses.
The in-challenge text tries to guide you to answer with a single generic type which passes the first argument to both trickOrTreat and restock.
typeHouse<Candy>={doorNumber: numbertrickOrTreat(): Candy;restock(items: Candy): void;}typeFirstHouse=House<"book"|"candy">typeSecondHouse=House<"toothbrush"|"mints">// ... same pattern for the rest
This could be enough, and that's totally enough type safety for cases like this. This does lose the doorNumber being exact though. So, here are two different routes to give the doorNumber to each house:
// Via a 2nd generic argumenttypeHouse<DoorNumber,Candy>={doorNumber: DoorNumbertrickOrTreat(): Candy;restock(items: Candy): void;}typeFirstHouse=House<1,"book"|"candy">typeSecondHouse=House<2,"toothbrush"|"mints">// ... same pattern for the rest
and
typeHouse<Candy>={doorNumber: numbertrickOrTreat(): Candy;restock(items: Candy): void;}// Via intersection types:typeFirstHouse=House<"book"|"candy">&{doorNumber: 1}typeSecondHouse=House<"toothbrush"|"mints">&{doorNumber: 2}
We started out by making types for passing the data around
typeMovies=typeofmoviesToShowtypeMovie={forKids: boolean}// Template strings literals to describe each tasktypeGet<Textendsstring>= `getVHSFor${capitalizeT}`
typeMakePopcorn<Textendsstring>= `makePopcornFor${capitalizeT}`
typePlay<Textendsstring>= `play${capitalizeT}`
// A union of the above literal typestypeTasks<Textendsstring>=Get<T>|MakePopcorn<T>|Play<T>
These gave us a set of primitives which could work together to create this whopper:
typeMakeScheduler<Type>={[FieldinkeyofTypeasTasks<Fieldextendsstring ? Field : never>]: ()=>void;};
This type uses the new as syntax for mapped types in TypeScript 4.1 to essentially map each field (Field) from the keys in the input type (Type) to the union Tasks above. This means that each field is converted into three templated literals:
For simplicities sake, we skipped typing the inside of the function - though the folks who did that, good work!
The second part added one simple constraint, but one that requires some work to get right. We wanted to take into account whether a movie was for kids of not inside the type system.
Our answer for this was to recreate the scheduler function above, and to add the logic for removing those types during the type mapping process.
Instead of returning a () => void, we inserted a conditional type in the return position which first checked if forKids is true in the original type. If it was, then it returned the function - otherwise it returned never. Returning never here would mean that the function would not exist - removing them from the mapping process.
The community came up with quite a few alternative takes which provided type safety inside the functions and used different routes like removing the non-kids moviekeys ahead of time.