DEV Community

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

Posted on

12 4

Type | Treat Challenge 2

Welcome to the second Type | Treat challenge! Today we will be deriving the types of pumpkins and busting ghosts!

Yesterday's Solution

Beginner/Learner Challenge

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

Link

Intermediate/Advanced Challenge

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

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:

type AllCandies = Exclude<ResultsFromHalloween, { candy: true }>
Enter fullscreen mode Exit fullscreen mode

Which is a great solution. Full link

The Challenge

Beginner/Learner Challenge

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?

Sharing

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 :)

Tiger Data image

🐯 🚀 Timescale is now TigerData: Building the Modern PostgreSQL for the Analytical and Agentic Era

We’ve quietly evolved from a time-series database into the modern PostgreSQL for today’s and tomorrow’s computing, built for performance, scale, and the agentic future.

So we’re changing our name: from Timescale to TigerData. Not to change who we are, but to reflect who we’ve become. TigerData is bold, fast, and built to power the next era of software.

Read more

Top comments (12)

Collapse
 
johnnhoj profile image
John Cheng •

In the advanced/intermediate challenge. It states that 'Bobby Tables' cannot have peanuts but to me, the solution seems to do the opposite and actually keep all the candies where there are peanuts 🀔

type HasPeanuts<A> = A extends { peanuts: true } ? A : never;
type AllCandiesWithoutPeanuts = HasPeanuts<AllCandies>
Enter fullscreen mode Exit fullscreen mode
Collapse
 
dungdung profile image
Houston •
type AllCandies = Extract<ResultsFromHalloween, {candy: true}>;
type AllTricks = Extract<ResultsFromHalloween, {trick: true}>;
type AllCandiesWithoutPeanuts = Exclude<AllCandies, {peanuts: true}>;
Enter fullscreen mode Exit fullscreen mode
Collapse
 
johnnhoj profile image
John Cheng • • Edited

I agree with your implementation. I was discussing the solution provided in this post :)
Maybe @gcrev93 can help me if I misinterpreted it

Thread Thread
 
dungdung profile image
Houston •

My bad, should have said something instead of just plopping down a solution - yes you are correct saying that:

type AllCandiesWithoutPeanuts = HasPeanuts<AllCandies>
Enter fullscreen mode Exit fullscreen mode

is the opposite of the solution. Something more correct would have the inverted type. I'm not sure how one would go from HasPeanuts to something like OmitsPeanuts but declaring it from scratch is straightforward:

type OmitsPeanuts<A> = A extends { peanuts: true } ? never : A;
Enter fullscreen mode Exit fullscreen mode
Collapse
 
mattiasbuelens profile image
Mattias Buelens •

Also, the proposed solution for AllCandies removes all candies from the union, instead of only keeping the candies.

type AllCandies = Exclude<ResultsFromHalloween, {candy: true}>
// type AllCandies = Toothpaste | Pencil
Enter fullscreen mode Exit fullscreen mode

This should probably use Extract instead of Exclude:

type AllCandies = Extract<ResultsFromHalloween, {candy: true}>
// type AllCandies = SnackBars | Gumballs | Apples | SnickersBar | Cookies
Enter fullscreen mode Exit fullscreen mode
Collapse
 
ricklove profile image
Rick Love •

Bobby is on his way to the hospital now. Thankfully somebody had an epi-pen ready.

Collapse
 
clandau profile image
Courtney •

"The need to use [number] is an interesting twist, because [0] or 1 would have worked just as well too. Doing this right would also have raised the typo in the original code."

I'm confused by the wording here. So, number AND [0] or [1] would work? So, you don't actually have to use number?

Collapse
 
orta profile image
Orta •

Yeah, you can use number or any literal like 1, 2, etc.

In this case, number however feels more right, because you're saying any number from the index, not a specific one.

Collapse
 
clandau profile image
Courtney •

Thanks! That makes sense.

Collapse
 
andrewgreenh profile image
Andreas Roth •

Here is my attempt :)

Collapse
 
dungdung profile image
Houston • • Edited

If we aren't allowed to change type Ghosts and function investigateReport which accepts an array of Ghosts, can we simply return false for both function areGods and function areEctoplasmic?

My implementation of function areDemons using user-defined type guard:

type Demons = Extract<Ghosts, {sendBackToHell(): void}>
function areDemons(ghosts:Ghosts[]): ghosts is Demons[] {
  return ghosts.every(ghost => (ghost as Demons).sendBackToHell !== undefined)
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
ricklove profile image
Rick Love •

Not if you want to live... and save NYC.

Gen AI apps are built with MongoDB Atlas

Gen AI apps are built with MongoDB Atlas

MongoDB Atlas is the developer-friendly database for building, scaling, and running gen AI & LLM apps—no separate vector DB needed. Enjoy native vector search, 115+ regions, and flexible document modeling. Build AI faster, all in one place.

Start Free

👋 Kindness is contagious

Explore this insightful write-up, celebrated by our thriving DEV Community. Developers everywhere are invited to contribute and elevate our shared expertise.

A simple "thank you" can brighten someone’s day—leave your appreciation in the comments!

On DEV, knowledge-sharing fuels our progress and strengthens our community ties. Found this useful? A quick thank you to the author makes all the difference.

Okay