Orta

# Type | Treat Challenge 4

Welcome to the fourth `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 four, which means going over the answers from yesterday and have 2 new challenges.

## Yesterday’s Solution

### Beginner/Learner Challenge

I wonder if we over-indexed on the difficulty here, and we’re interested if you dropped off somewhere through this task because we had less submissions than usual for this challenge. The goal was to have you build out a template string literal type which accounted for string input which roughly matched how CSS’s stringy variables worked.

You started with:

`type Length = string`

Which accepts all possible strings, next we show some examples which should always fail. The key one here being that an empty string should fail: `""`. Next we provided some valid input for you to work with:

```type Length = `\${number}in`

// Works with:
req("0in")
req("12in")```

Giving you a sense that a number can be used in the template slot – which allows for all sorts of possibilities.

Next we gave samples with different prefixes, so `"in"` and `"cm"` would need to be handled. To get that right, you would need to use a union:

```type Unit = "cm" | "in"
type Length = `\${number}\${Unit}`

// Works with:
req("0in")
req("12in")
req("1.5cm")
req("20cm")```

Next we threw a curve ball – `"0"` should also be acceptable, this is a bit of a curve ball, but also it’s a bit of a trick:

```type Unit = "cm" | "in" | ""
type Length = `\${number}\${Unit}`

// Works with:
req("0in")
req("12in")
req("1.5cm")
req("20cm")
req("0")```

The lack of a unit is just an empty string unit! Only one more thing now, and that is allowing a space inbetween the number and unit. This could be done via another type also:

```type Unit = "cm" | "in" | ""
type Space = " " | ""
type Length = `\${number}\${Space}\${Unit}`

// Works with:
req("0in")
req("12in")
req("1.5cm")
req("20cm")
req("0")
req("12 cm")
req("14 in")```

That was is for the easy parts of the challenge. It’s pretty tricky, because it requires that you understand that `number` can be anything in the template string and to understand how a union can allow for different types of strings inside the type. That’s all in the main docs, but it could be a lot of ideas to learn at once.

This challenge also had a set of complications, cases where the version of the the `Length` type we expected people to build would provide interesting edge cases:

```req(`\${0.3e21}cm`)
req("-12 cm")
req(`\${Infinity}cm`)
req(`\${NaN}cm`)```
Click to learn about these cases
`req(`\${0.3e21}cm`)`

Acted as a potential clue to an alternative answer for these failing cases:

```req(`\${Infinity}cm`)
req(`\${NaN}cm`)```

Because `number` can be switched out with `bigint` in the type of `Length`:

```- type Length = `\${number}\${Space}\${Unit}`
+ type Length = `\${bigint}\${Space}\${Unit}````

This meant you couldn’t pass in `Infinity` or `NaN` but also broke `req("1.5cm")` because you can’t have point values. This could be fixed via:

`type Length = `\${bigint}\${Space}\${Unit}` | `\${bigint}.\${bigint}\${Space}\${Unit}``

Which describes both possible cases with a “.” and without. This technique still doesn’t handle the `req("-12 cm")`, and actually, it introduces a whole new problem: `req("-12.-12cm")` is allowed!

We spotted a good answer from @danvdk which revolved around using string manipulation instead, by introducing a `Digit` type:

```type Whitespace = '' | ' ';
type Unit = 'in' | 'cm';
type Digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9';
type Length = `\${Digit}\${number | ''}\${Whitespace}\${Unit}` | '0';```

This solution correctly handles the case of `req("-12 cm")` but via that `number` would allow something like `req("1-22 cm")` – which you can pretend is to handle an input range. It wouldn’t be hard to take this solution and reasonably cover additional edge cases. Very cool solution.

The intermediate challenge was on type literals mixed with generics functions. The challenge started with this function:

```function makeTitle(str: string) {
return "<spooky>" + str.toUpperCase() + "</spooky>"
}```

The goal was to keep track of string literals through this function. To do this, you need to switch the `str: string` to be a type argument:

```function makeTitle<Str>(str: Str) {
return "<spooky>" + str.toUpperCase() + "</spooky>"
}```

You know that the type argument has to be a string, which you can tell TypeScript via `<Str extends string>`, then you can re-use the `Str` in the return position:

```function makeTitle<Str extends string>(str: Str): `<spooky>\${Uppercase<Str>}</spooky>` {
return "<spooky>" + str.toUpperCase() + "</spooky>"
}```

You’d think this would be it, but `str.toUpperCase` actually converts the `str` to a string! Tricky, you’d need to think creatively here and you have three options:

1. Use an `as` because you know better than the compiler:

```function makeTitle<Str extends string>(str: Str): `<spooky>\${Uppercase<Str>}</spooky>` {
const shouty = str.toUpperCase() as Uppercase<Str>
return `<spooky>\${shouty}</spooky>`
}```
2. Override `toUpperCase` to support template literals:

```interface String {
toUpperCase<T extends string>(this: T) : Uppercase<T>
}```
3. Or create a new function which supports template literals.

This would take the `"party"` used on line 19 and convert it to `"<spooky>PARTY</spooky>"`. That change would remove the compiler error on `addTadaEmoji`.

The second part was about re-using the type parameters inside argument for the function. The challenge started with:

```function setupFooter(str: string) {
// validate string etc
return {
name: str.split(",")[0],
date: str.split(",")[1],
}
}```

Would lose string literals passed in as `str`. You knew ahead of time that there were three separate parts of information you were interested in:

`function setupFooter<Name extends string, Date extends string, Address extends string>(str: string) {`

These could then be used inside the replacement for `string`:

`function setupFooter<Name extends string, Date extends string, Address extends string>(str: `\${Name},\${Date},\${Address}`) {`

Which would correctly set up these variables for re-use later:

```function setupFooter<Name extends string, Date extends string, Address extends string>(str: `\${Name},\${Date},\${Address}`) {
// validate string etc
return {
name: str.split(",")[0] as Name,
date: str.split(",")[1] as Date,
}
}```

Successfully completing this challenge would show that `name`, `date` and `address` were not `string` but the strings passed in.

## The Challenge

### Beginner/Learner Challenge

Run a set of pumpkin competitions

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

• Leave us a comment with your feedback on the dev.to post, or in this post.

### Best Resources for Additional Help

Happy Typing 🙂

• Peter Parker

I’m curious why just having the following type definition (ie no definition for Space)

```type Unit = "cm" | "in"
type Length = `\${number}\${Unit}`
```

Doesn’t cause a compiler error in the playgroup for the whitespace inputs

```req("12 cm")
req("14 in")```
• Rifat Nabi

> It feels right that if you pass “0”, you should be able to go unit-less

I thought the requirement was to only allow 0 w/o unit, not any number. So, I went with:

`type Length = `\${number}\${Unit}` | '0'`

The solution in this post also allows:

`req("12")`