Understanding Typescript utility Types
TypeScript provides a set of built-in utility types to assist with common type transformations. These utility types can help you write safer and more scalable code by eliminating the need to write complex type definitions.
As I keep finding that most TypeScript devs don't know about them and I found them such a powerful tool, I decided to write this post, where we'll explore some of the most commonly used utility types in TypeScript and how they can be used to improve your code.
Readonly
The Readonly
type, as it's name suggest, is used to make properties of an object read-only. This means that the values of these properties cannot be changed once they are set. If you try to update them, you will end up with a build error similar when you try to update a constant.
interface Point {
x: number;
y: number;
}
const p: Readonly<Point> = { x: 10, y: 20 };
// Error: Cannot assign to 'x' because it is a read-only property.
p.x = 30;
This type can be especially useful when working with APIs that return data that should not be modified, or when you want to ensure that certain properties of an object cannot be changed accidentally.
Partial
The Partial
type makes all properties of an object optional. This is useful when you need to create objects with some, but not all, properties set.
interface Point {
x: number;
y: number;
}
const p: Partial<Point> = {};
// No error: x and y are both optional.
This type can be especially useful when working with APIs that allow partial updates to objects, or when you want to create objects with only a subset of properties set initially. I use this several time working with updates for example with Firestore where you only send the properties that you want to update in an object and you don't need to send all the properties all over again.
Pick
This is one is one of my favorite ones. The Pick type is used to select a subset of properties from an object. This can be useful when you want to create a new type that only includes a specific set of properties from an existing type. It's specially usefull when you have big interfaces with lot of properties but you only need some of them in an scenario. You could also just duplicate the interface but the great thing of this instead of just duplicating the interface is that you will see the errors easily if you need to do a refactor changing types or property names.
interface Point {
x: number;
y: number;
z: number;
}
type Point2D = Pick<Point, "x" | "y">;
const p: Point2D = { x: 10, y: 20 };
// Error: Property 'z' does not exist on type 'Point2D'.
p.z = 30;
This type can be especially useful when working with APIs that return data with more properties than you need, or when you want to create a new type that only includes a specific subset of properties from an existing type.
Just remember that this kind of utils will not remove the properties of an existing object so if you have a Point
and then you end up with a Point2D
, you will still have the z value in memory. You will need to create a mapper function that only copies the properties that you need.
Omit
Very similar to the Pick
type, you have the Omit
type, which is used to exclude specific properties from an object type. Again, this is useful when you want to create a new type that is based on an existing type, but in this case, without certain properties.
interface Point {
x: number;
y: number;
z: number;
}
type Point2D = Omit<Point, "z">;
const p: Point2D = { x: 10, y: 20 };
// Error: Property 'z' does not exist on type 'Point2D'.
p.z = 30;
The same notes as for Pick
, it's a super useful type but keep in mind that it's only a type, you still need to have a mapper or similar not to expose those properties in runtime.
Conclusion
TypeScript utility types are a powerful tool that can help you write safer and more maintainable code. By using these utility types, you can reduce the amount of manual type definitions you need to write, which can help to make your code more concise and readable.
It's important to note that these utility types are just a small subset of what TypeScript has to offer. There are many other utility types available, each with its own specific use case. As you continue to work with TypeScript, it's worth taking the time to explore these types and see how they can be used to improve your code. You can find them all here.
So whether you're new to TypeScript or a seasoned veteran, the utility types are a great tool to have in your toolbox. Give them a try and see how they can help you write better code!