We made it to day number ten of our TypeScript challenge journey until Christmas!
In today's prompt, we'll use a couple of topics that we have already covered but with a small twist that will show us a different usage of them.
Hint: We'll revisit conditional types, string literals, and infer.
Let's get to it 💪🏻
Day ten - Dec 10th
I strongly encourage you to try the first challenge on your own before reading the solution here.
Challenge: Christmas Street Suffix Tester
The challenge
It's a little known fact that Santa's reindeer are orienteering experts. They're very particular, actually.
To do this work well, they need to do some basic validation on the addresses. There were hopes among some reindeer to introduce a validation library this year, but there was simply too much infighting. It's kindof a mess. You see..
Comet and Vixen want to use Zod because they heard from a YouTuber they like that it's the best (they haven't actually looked ito anything else).
Cupid and Rudolph are simply too used to JSON Schema with AJV. They don't want to learn a new thing. They both had popular webpack plugins in the past that are no longer used by anyone and now they're a little bitter about change (in general).
Then you have Prancer. Prancer doesn't see the point in validation. Prance feels that validating inputs pollutes the code with type gymnastics that add ever so little joy to the development experience. Yep. Even the reindeer have one of these on their team in 2023.
Meanwhile, Blitzen is pushing hard for typia because it's so fast (naturally).
Dancer and Donner don't seem to ever be able to articulate their opinions and usually just follow the rest of the group.
The Type's API
So, for this year.. nothing fancy. We'll have to just write a StreetSuffixTester
from scratch.
This type will take two generic arguments. The first is for the street, and the second is for the suffix we're testing against.
If the street ends with the suffix then the type should return true
(otherwise, false
).
The code to complete
type StreetSuffixTester = unknown;
The tests
import { Expect, Equal } from 'type-testing';
type test_0_actual = StreetSuffixTester<'Candy Cane Way', 'Way'>;
type test_0_expected = true;
type test_0 = Expect<Equal<test_0_expected, test_0_actual>>;
type test_1_actual = StreetSuffixTester<'Chocalate Drive', 'Drive'>;
type test_1_expected = true;
type test_1 = Expect<Equal<test_1_expected, test_1_actual>>;
type test_2_actual = StreetSuffixTester<'Sugar Lane', 'Drive'>;
type test_2_expected = false;
type test_2 = Expect<Equal<test_2_expected, test_2_actual>>;
The solution
type StreetSuffixTester<Street extends string, Suffix extends string> =
Street extends `${infer StName}${Suffix}` ? true : false;
Define a Generic type: This generic type takes two types that are stings:
<Street extends string, Suffix extends string>
Conditional type to return a boolean:
Street extends
${infer StName}${Suffix}? true : false
. This statement checks if theStreet
type ends with the specifiedSuffix
type. If it does, it evaluates totrue
; otherwise, it evaluates tofalse
.
At this point, we have extensively talked about conditional types and even touched a bit on infer in this TypeScript fundamental series.
The trick part to solve this prompt is ${infer StName}${Suffix}
.
Let me try to break down what is happening here:
${infer StName}
: Theinfer
here is telling TypeScript, 'Hey, you figure out and remember whatever is at this spot in the string, in this case, I called itStName
. It is just a placeholder value.${Suffix}
: This part represents the genericSuffix
type that you provided. It's the suffix we want to check if the street ends with.
Putting them together ${infer StName}${Suffix}
means we are looking for a string that ends with the specified suffix, and we are telling TypeScript to infer the prefix of that string and represent it as StName
.
If your IDE highlights unused code differently, you might have noticed that StName
is not used in the type definition. That is okay since we don’t actually care about it, it just helps us validate our condition.
If you don’t have to have that issue you can replace StName
for a _
.
Hope you enjoyed today's post, thanks for sticking around, these ten days have flown by. One more day of leveling up our TypeScript fundamentals and one day closer to Christmas.
If you like this content consider checking out what I post on Twitter/X 🐦