Unlocking TypeScript: Mastering Mapped Types for Data Filtering
Written on
Understanding Mapped Types in TypeScript
TypeScript is an exceptional tool for enhancing data integrity and writing clean code. In this article, we'll delve into the application of Mapped Types to tackle a realistic coding challenge. Let's dive right in!
The Challenge
In this scenario, we utilize the country-to-currency package to create associations between country codes and their respective currencies. Here's how it looks:
import countryToCurrency, {
Currencies,
Countries as CountryCodes,
} from "country-to-currency";
// For clarity
type CountryToCurrency = typeof countryToCurrency;
// type Currencies = "GBP" | "USD" ...
// type CountryCodes = "GB" | "US" ...
// type CountryToCurrency = { GB: "GBP", US: "USD" ... }
So, what’s the issue?
It would be beneficial to retrieve specific CountryCodes corresponding to an input Currency, all while ensuring type safety. This code snippet illustrates the requirement:
type Result = CountryCodesFromCurrency<"GBP">;
// Result Type: "GB" | "GG" | "IM" | "JE"
To clarify this concept further, let’s visualize the problem with a diagram:
Reverse Mapping Currencies to Country Codes
We aim to reverse-map currencies to their associated country codes.
The Solution
Fortunately, Mapped Types offer a sophisticated solution for this. They enable the creation of new types based on existing ones by applying transformations to each property.
// Here's a fundamental example of a Mapped Type
// This converts all values to boolean while preserving the keys.
type OptionsFlags = {
[Property in keyof Type]: boolean;
};
To accomplish the reverse mapping from Currencies to CountryCodes, we need to follow this logic:
- Define a Mapped Type.
- For each key (CountryCode) in our CountryToCurrency mappings, check if the associated value matches the input Currency. If it does, retain this property; if not, eliminate it by setting the key type to never.
Here's how this looks in code:
type FilterMappingsFor = {
[CountryCode in keyof CountryToCurrency
as CountryToCurrency[CountryCode] extends ThisCurrency
? CountryCode : never]: CountryToCurrency[CountryCode]
};
We establish a Mapped Type called FilterMappingsFor that takes ThisCurrency as an argument to fetch the relevant country codes. We iterate through each CountryCode. For each, we compare its corresponding currency (CountryToCurrency[CountryCode]) against our input ThisCurrency. If they align, we preserve the property; if they don’t, we set the key to never, effectively removing it.
We’re almost there!
Currently, FilterMappingsFor provides the correct mappings, but we only need the CountryCodes. Let’s create a new type, CountryCodesFromCurrency, to extract the keys from our filtered mappings:
type CountryCodesFromCurrency =
keyof FilterMappingsFor;
Finally, we can validate this by defining new types Result1 and Result2 as follows:
type Result1 = CountryCodesFromCurrency<"GBP">;
type Result2 = CountryCodesFromCurrency<"NIO">;
// Output1: "GB" | "GG" | "IM" | "JE"
// Output2: "NI"
If you found this article helpful, don't forget to subscribe, clap, comment, and connect with me!
Featured Resources
- Figma Home: The UI design tool I use for all my projects.
- Figma Professional: The ultimate UI design tool you will ever need.
- FigJam: An intuitive tool for brainstorming and diagramming.
- Notion: The tool that helps me organize my entire life.
- Notion AI: An AI tool that enhances your Notion experience.
References
- TypeScript Mapped Types Documentation
- country-to-currency package
Chapter 1: TypeScript Error Handling
In this section, we will address some common TypeScript errors and how to resolve them effectively.
The first video discusses common TypeScript errors and their solutions:
Chapter 2: Mistakes to Avoid in TypeScript
This chapter highlights frequent mistakes made by junior developers in TypeScript and how to sidestep them.
The second video outlines essential mistakes every junior developer should avoid: