Welcome to the third Type | Treat
challenge! Today we will be going over yesterday's answers and diving into some new problems to solve.
Yesterday's Solution
Beginner/Learner Challenge
The typeof
operator is one of those small tools which helps you avoid duplication, if you already have the type in the runtime - why not re-use it?
- type Pumpkin = any
+ type Pumpkin = typeof pumpkin
- type PumpkinFromFunction = any
+ type PumpkinFromFunction = ReturnType<typeof Pumpkin>
Intermediate/Advanced Challenge
This one is a tricky one. Made a bit harder by an accident of not including every ghost in the Ghosts
type.
The challenge aimed give a subtle nod to day 1's question, where the rest of the challenge becomes easier if you first make a set of types via conditionals or Extract.
type Gods = Extract<Ghosts, { god: true }>;
type Demons = Extract<Ghosts, { demon: true }>;
type EctoPlasmics = Extract<Ghosts, { ectoplasmic: true }>;
This does actually work in the current challenge, even though it's not quite right. From there, you can create user-defined type guards to change the code flow in the main algorithm to work as expected.
function areGods(ghosts: Ghosts[]): ghosts is Gods[] {
return ghosts.every(ghost => "god" in ghost);
}
function areDemons(ghosts: Ghosts[]): ghosts is Demons[] {
return ghosts.every(ghost => "demon" in ghost);
}
function areEctoPlasmic(ghosts: Ghosts[]): ghosts is EctoPlasmics[] {
return ghosts.every(ghost => "ectoplasmic" in ghost);
}
That said, let's try work within the constraint of 'maybe the TypeScript team know what they are doing, and this challenge is meant to be this way?!' - which is kinda provably false
from this challenge.
In the TypeScript structural type system, you don't really need to know much more than is required, and you could safely create a singular type
for God
, Demon
and EctoPlasmics
, then declare an array of those types:
type God = Ghosts & { god: true };
type Demon = Ghosts & { demon: true, sendBackToHell(): void };
type EctoPlasmic = Ghosts & { ectoplasmic: true };
function areGods(ghosts: Ghosts[]): ghosts is God[] {
return ghosts.every(ghost => "god" in ghost);
}
function areEctoPlasmic(ghosts: Ghosts[]): ghosts is EctoPlasmic[] {
return ghosts.every(ghost => "ectoplasmic" in ghost);
}
function areDemons(ghosts: Ghosts[]): ghosts is Demon[] {
return ghosts.every(ghost => "demon" in ghost);
}
That type-safety is enough for the algorithm, but could bite you later on because Ghosts & [x]
makes any other property optional.
If you are going for minimalism, here's a terse answer in 3 one-liners which takes into account usage inside the algorithm:
const areDemons = (ghosts: Ghosts[]): ghosts is Extract<Ghosts, { demon: true }>[] => ghosts.every(ghost => "demon" in ghost);
const areEctoPlasmic = (ghosts: Ghosts[]): ghosts is Extract<Ghosts, { ectoplasmic: true }>[] => ghosts.every(ghost => "ectoplasmic" in ghost);
const areGods = (ghosts: Ghosts[]): boolean => ghosts.every(ghost => "god" in ghost);
@igorbek managed to get it to two lines!
The Challenge
Beginner/Learner Challenge
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?
Intermediate/Advanced Challenge
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?
Share
Be sure to submit your solution by using the Share button in the TypeScript playground.
Then go to Twitter, and create a tweet about the challenge, add the link to your code and mention the TypeScript page (@typescript)
Need Extra Help?
If you need additional help you can utilize the following:
- The TypeScript Handbook
- TypeScript Discord Page
- The comments on each Dev.to posts!
Happy Typing :)