Mastering TypeScript Mapped Types: From Basics to Advanced Concepts
Written on
Chapter 1: Introduction to Mapped Types
Mapped types provide a mechanism to convert the properties of one type into another. They function similarly to array methods like map and filter, but are specifically tailored for types.
To grasp the concept of mapped types, it's essential to familiarize yourself with several key TypeScript features:
- keyof Operator: This operator yields a union type of all the property names in a given type. For instance, keyof { name: string, age: number } results in 'name' | 'age'.
- in Operator: This operator allows you to iterate over the properties of a type. For example, Key in keyof Type loops through each property of Type.
- as Keyword: This keyword assigns a new identifier to a type. For example, Type as NewType maps Type to NewType.
- never Type: This type signifies the absence of a value and is often employed in conditional types to exclude certain properties.
- ? Operator: This operator marks a property as optional. For instance, Key?: Type indicates that Key is optional.
- readonly Modifier: This modifier restricts a property from being altered. For example, readonly Key: Type makes Key immutable.
- Union Types: These types combine multiple types into one, exemplified by Type1 | Type2, which can represent either Type1 or Type2.
- Utility Types: Built-in types that offer common transformations on other types. For instance, Partial is a utility type that renders all properties of a type optional.
Let's delve into some examples to see how these concepts play out.
Section 1.1: Transforming Properties into Booleans
In certain situations, we may want to convert the properties of one type into booleans. This can be accomplished through mapped types.
Consider the example below, where we transform the User type into a new type UserToBoolean, where all properties are of type boolean. The expression Key in keyof User iterates over each property of User, creating a corresponding property with the boolean type.
Isn’t this quite akin to the map function in JavaScript? =)
Section 1.2: Making Properties Optional
Typically, the Partial utility type is used to make all properties of a type optional. However, we can also leverage mapped types for this purpose.
In this instance, we transform the properties of User into optional attributes by appending a ?. We utilize User[Key] to fetch the type of each property from User.
Subsection 1.2.1: Implementing Partial with Generics
It’s also feasible to implement the Partial utility type using generics.
Chapter 2: Advanced Mapped Types Techniques
Section 2.1: Enforcing Required Attributes
Mapped types can also be employed to convert optional properties of a type into required ones by placing a - before the ?.
In this section, we iterate through each property of User, using User[Key] to maintain the type while ensuring that the properties are now required.
Section 2.2: Making Properties Readonly
We can use mapped types to declare all properties of a type as readonly by incorporating the readonly modifier before the property name. An alternative approach is using the built-in Readonly utility type, which can also be constructed using generics and mapped types.
Section 2.3: Excluding Specific Properties
While the Omit utility type allows for the removal of certain properties, mapped types can achieve similar outcomes.
In previous examples, we have illustrated how mapped types can transform properties from one type to another.
Section 2.4: Creating Types with Conditional Properties
Utilizing conditional types, we can formulate a new type that only retains properties of a specific type.
For instance, we create a type OnlyStringProperties that encompasses properties from Type that are of type string, checking with a conditional type.
Section 2.5: Generating New Property Names via Template Literals
Imagine constructing a new type that retains all properties but prefixes them with 'get' and capitalizes their names. Template literal types enable us to accomplish this.
In this example, string & Key ensures that Key is a string. We then use the Capitalize utility type to capitalize the property name and template literals to establish the new property name.
We can also devise types for setters with a void return type. By amalgamating both types, we can create a type that encompasses both getters and setters.
Conclusion
Mapped types present a robust feature within TypeScript, empowering developers to execute complex type transformations. They allow you to:
- Transform Properties: Adjust the types of existing properties within a type.
- Add or Remove Properties: Introduce new properties or filter out existing ones.
- Control Optionality and Readonly Status: Designate properties as optional or immutable.
- Create Dynamic Types: Formulate new types using conditional types and template literals for intricate scenarios (such as generating getters and setters).
While built-in utility types like Partial, Readonly, and Omit offer convenient solutions, mapped types highlight the foundational principles, granting you greater control over your types as per your requirements.
By mastering mapped types and the techniques illustrated, you will enhance your ability to manipulate types, leading to clearer, more predictable, and maintainable code.
Enjoyed the read? If so, please give a high-five by hitting the like button to inspire more engaging content!