DEV Community

Cover image for Type | Treat Challenge 3
Gabrielle Crevecoeur for typescript

Posted on

Type | Treat Challenge 3

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>
Enter fullscreen mode Exit fullscreen mode

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 }>;
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

@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?

Help out here

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?

Refactor

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:

Happy Typing :)

Top comments (2)

Collapse
 
levenleven profile image
Aleksey Levenstein
type Spots = typeof trunkOrTreatSpots[number];

type TrunkOrTreatResults = {
    [Spot in Spots]: {
        done: boolean,
        who: string,
        loot: Record<string, any>
    }
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
andrewgreenh profile image
Andreas Roth

I hope I interpreted the tasks correctly.