TypeScript's Utility Types: A developer's guide to strong typing
TypeScript's utility types are a powerful tool for developers looking to improve the robustness and maintainability of their code.
TypeScript's utility types are a powerful tool for developers looking to improve the robustness and maintainability of their code. TypeScript comes with a large number of types that can help with some common type manipulation, usually referred to as utility types.
With utility types, developers can create new types, extract specific properties from existing types, or even remove properties from existing types. One of the main benefits of strong typing is that it allows for better code readability and maintainability.
The purpose of this guide is to provide developers with a comprehensive understanding of TypeScript's utility types and how to use them effectively in their projects.
The guide will cover the various types of utility types available in TypeScript, such as "Pick", "Exclude", "Omit", and "Partial”.
This guide is intended for developers of all skill levels, from those who are new to TypeScript to experienced developers looking to improve their knowledge of the language.
By the end of this guide, you will have a solid understanding of how to use TypeScript's utility types to improve the quality and maintainability of your code.
Commonly used utility types
Understanding TypeScript's Utility Types is an essential aspect of working with the TypeScript programming language. The most commonly used utility types include "Pick", "Exclude", "Omit", "Partial" and more.
Using TypeScript Pick
type
The "Pick" type allows developers to extract a specific set of properties from an existing type and create a new type that only includes those properties. This can be useful for situations where you have an object with multiple properties, but you only need to use a specific subset of those properties in a particular function.
interface Person {
name: string; // Property name
age: number; // Property age
address: string; // Property address
}
type PersonInfo = Pick<Person, "name" | "age">; // Use Pick utility type to select properties "name" and "age" from Person interface and create new type PersonInfo
const person: PersonInfo = { // Define person as type of PersonInfo
name: "John Doe",
age: 30
};
Using TypeScript Exclude
type
The "Exclude" type allows developers to remove specific properties from an existing type. This can be useful for situations where you have an object with multiple properties, but you only need to exclude some of those properties in a particular function.
interface Person {
name: string; // Property name
age: number; // Property age
address: string; // Property address
}
type PersonInfo = Exclude<Person, "address">; // Exclude "address" property from Person interface and create new type PersonInfo
const person: PersonInfo = { // Define person as type of PersonInfo
name: "John Doe",
age: 30
};
Using TypeScript Omit
type
Omit is the opposite of Pick, it allows you to remove specific properties from an existing type and create a new type that does not include those properties*,*
Note: unlike pick which allows you to extract specific properties from an existing type and create a new type that only includes those properties.
It is useful when you have an object with multiple properties, but you only need to exclude some of those properties in a particular function. I hope you understand it well because it took me a lot of time to learn it, but you are smart and not dumb. Don’t worry code will omit your confusion.
interface Person {
name: string; // Property name
age: number; // Property age
address: string; // Property address
}
type PersonInfo = Omit<Person, "address">; // Omit "address" property from Person interface and create new type PersonInfo
const person: PersonInfo = { // Define person as type of PersonInfo
name: "John Doe",
age: 30
};
In addition to these commonly used utility types, TypeScript also provides a number of advanced utility types that can be used for more specific use cases. These include types such as "Readonly
", "Required
", "Record
", and "Extract
", among others.
Advanced Usage of Utility Types
Advanced usage of utility types in TypeScript can involve combining multiple utility types to create more complex types. Here are a few examples of how to use utility types in advanced ways:
Using Intersection
to create a type that includes properties from multiple types:
type T1 = { a: number, b: string };
type T2 = { b: boolean, c: string };
type T3 = T1 & T2;
Using Mapped Types
to create a new type with modified properties:
type T1 = { a: number, b: string };
type T2 = { [P in keyof T1]: T1[P] | null }
Using Conditional Types
to create a type that changes based on certain conditions:
type T1 = { a: number, b: string };
type T2 = { c: number, d: string };
type T3 = Exclude<keyof T1 | keyof T2, keyof T1>;
Combining utility types to create custom types
When you want to create a new type that combines properties from multiple existing types. The Intersection utility type allows you to easily create new types that include properties from multiple other types.
interface Person {
name: string; // Property name
age: number; // Property age
address: string; // Property address
}
interface Job {
title: string; // Property title
salary: number; // Property salary
}
// Use the Intersection utility type to create a new type that includes properties from both Person and Job interfaces
type Employee = Person & Job;
const employee: Employee = {
name: "John Doe",
age: 30,
address: "123 Main St",
title: "Developer",
salary: 60000
};
It is also possible to use other utility types to further refine this custom type. For example, you might use the "Pick" utility type to select specific properties from the "Employee" type or the "Omit" utility type to exclude certain properties from the "Employee" type.
Best Practices for Using Utility Types
When using utility types, it is important to keep in mind that they are a tool to help you achieve strong typing in your application, but they should not be overused.
Here is an example close to a real use case,
interface User {
name: string;
age: number;
address: string;
email: string;
phone: string;
}
// Define a custom type that is specific to the needs of the application
type ContactableUser = {
name: User['name'],
email: User['email']
}
const user: ContactableUser = {
name: 'John Doe',
email: 'johndoe@example.com',
};
It is often better to create a custom type that is specific to the needs of your application rather than trying to use multiple utility types together. This can make your code more readable and easier to maintain. These are some common mistakes when using utility types
Not specifying the correct types: It's important to make sure that you are passing the correct types to the utility types. For example, if you're using
Pick
to select properties from an object, make sure that the properties you're selecting actually exist on that object.Not using the correct syntax: Utility types often require a specific syntax to be used correctly. For example, when using Exclude, you need to pass in two types: the original type and the type of the properties to be excluded.
Not understanding the behavior of the utility type: Each utility type behaves differently and it's important to understand how they work before using them. For example, Omit will remove properties from an object, but it will not change the type of the remaining properties.
To prevent these mistakes, it is important to read the official TypeScript documentation and understand the behavior of each utility type.
Additionally, mostly I do, you should use inline comments in your code to document your use of the utility types and to make it easier for other developers to understand your code.
Strategies for debugging code with utility types
Debugging and troubleshooting code that uses TypeScript's utility types can be challenging, but there are a few strategies you can use to make it easier. One of the most important things to understand is the error messages that TypeScript will provide. These messages often include the specific utility type that is causing the issue, so understanding what these types do can help you quickly identify and fix the problem. Okay! let's learn some debugging tips
Using the console.log()
function
Logging the values of variables that use utility types can help you understand what is happening with your data. This can be especially useful when working with complex utility types such as Intersection
and Union
. console.log
is considered to be a weapon in the JavaScript world.
Using the typeof
operator
The typeof
operator can be used to check the type of a variable, which can be useful when working with utility types that change the type of a variable, such as Exclude
and Omit
.
Using the in
operator
The in
operator can be used to check if a property exists in an object, which can be useful when working with utility types that remove properties from an object, such as Pick
and Omit
.
Using the keyof
operator
The keyof
operator can be used to check the keys of an object, which can be useful when working with utility types that change the keys of an object, such as Record
.
Using the type
alias
Defining a type alias for a complex utility type can make the code more readable and also make it easier to debug.
Using a type-checking tool
There is a ts-check
in your editor, which can provide real-time type checking and error messages, which can help you catch issues with your types early on.
If all else fails, consult the TypeScript documentation, which provides detailed explanations of each utility type and how to use them correctly.
Closing words
I hope you like the article and found it informative and helpful. TypeScript's utility types are a powerful tool for improving the safety and maintainability of your code.
By understanding the different utility types available and how to use them, you can create more robust and error-free code.
Additionally, by following best practices and having strategies for debugging and troubleshooting, you can ensure that any issues that arise can be quickly identified and resolved.
Overall, learning to effectively use utility types can greatly benefit any developer working with TypeScript.
A developer lives in a state of learning, some times he suffers from imposter syndrome. I am also a developer and learner. I wrote about what I learned. I want to get you out of syndrome. Here is the newsletter that will help you to learn new concepts. There you go
I am open to feedback, suggestion, and improvements. Thanks for your precious time.