Utility Types in TypeScript
This article explains utility types in TypeScript.
YouTube Video
Utility Types in TypeScript
TypeScript utility types are convenient tools for creating new types based on existing types. This allows for more flexible type definitions and increases code reusability. Here, we will explain commonly used utility types in detail and discuss how to use each one with sample code.
Partial<T>
Partial<T>
makes all properties of an object type optional (allowing undefined
). It is useful when you want to use only some of the properties that the original type has.
1interface User {
2 id: number;
3 name: string;
4 age: number;
5}
6
7function updateUser(user: Partial<User>) {
8 console.log(user);
9}
10
11updateUser({ name: "Alice" }); // Updates only 'name'
- In the example above, using
Partial<User>
makes all properties of theUser
type optional. Therefore, in theupdateUser
function, you can pass only a subset of the properties.
Required<T>
Required<T>
makes all properties, including optional ones, required. It is used when you want to convert properties that are optional to be required.
1interface User {
2 id: number;
3 name?: string;
4 age?: number;
5}
6
7function createUser(user: Required<User>) {
8 console.log(user);
9}
10
11// createUser({ id: 1 }); // Error: 'name' and 'age' are required
12createUser({ id: 1, name: "Alice", age: 25 });
- By using
Required<User>
, properties such asname
andage
are treated as required.
Readonly<T>
Readonly<T>
makes all properties of an object read-only. This prevents the values of the object from being changed.
1interface User {
2 id: number;
3 name: string;
4}
5
6const user: Readonly<User> = {
7 id: 1,
8 name: "Alice"
9};
10
11// user.id = 2; // Error: 'id' is read-only
12console.log(user);
- By using
Readonly<T>
, you can protect an object's properties from being altered. It is effective when you want to prevent data from being accidentally modified during development.
Record<K, T>
Record<K, T>
creates a map type with specified key and value types. K
is the type for the keys (such as string
or number
), and T
is the type for the values.
1type Roles = "admin" | "user" | "guest";
2interface ReadWritePermissions {
3 read: boolean;
4 write: boolean;
5}
6
7const rolePermissions: Record<Roles, ReadWritePermissions> = {
8 admin: { read: true, write: true },
9 user: { read: true, write: false },
10 guest: { read: false, write: false },
11};
12
13console.log(rolePermissions);
Record<K, T>
is useful when you want to define key-value pairs. In the example above, permissions are defined based on user roles.
Pick<T, K>
Pick<T, K>
extracts only specified properties from an object type. You can create a new type by extracting only the necessary properties.
1interface User {
2 id: number;
3 name: string;
4 age: number;
5}
6
7type UserSummary = Pick<User, "id" | "name">;
8
9const summary: UserSummary = {
10 id: 1,
11 name: "Alice"
12};
13
14console.log(summary);
- By using
Pick<T, K>
, you can extract specific properties from an object type and treat them as a new type. For example, onlyid
andname
are extracted in the example above.
Omit<T, K>
Omit<T, K>
excludes specified properties from an object type. This is the opposite operation of Pick
.
1interface User {
2 id: number;
3 name: string;
4 age: number;
5 email: string;
6}
7
8type UserWithoutEmail = Omit<User, "email">;
9
10const userWithoutEmail: UserWithoutEmail = {
11 id: 1,
12 name: "Alice",
13 age: 25
14};
15
16console.log(userWithoutEmail);
- By using
Omit<T, K>
, you can create a new type by excluding specified properties. In the example above, theemail
property is excluded.
Exclude<T, U>
Exclude<T, U>
creates a new type by removing type U
from union type T
. It is used when you want to remove a specific type.
1type Status = "active" | "inactive" | "pending";
2type ExcludedStatus = Exclude<Status, "pending">;
3
4const userStatus: ExcludedStatus = "active"; // "pending" is excluded, so it cannot be chosen
5console.log(userStatus);
- By using
Exclude<T, U>
, you can remove unnecessary types within a union type. In the example above, since"pending"
is excluded, only"active"
or"inactive"
can be selected.
Extract<T, U>
Extract<T, U>
extracts the portions that match type U
within union type T
. It is useful when you want to extract only a specific type.
1type Status = "active" | "inactive" | "pending";
2type ActiveStatus = Extract<Status, "active" | "pending">;
3
4const userStatus: ActiveStatus = "active"; // "inactive" cannot be chosen
5console.log(userStatus);
Extract<T, U>
performs the opposite operation ofExclude
. In the example above, only"active"
and"pending"
can be selected.
NonNullable<T>
NonNullable<T>
creates a type with null
and undefined
excluded. It is helpful when you want to exclude these values from optional types.
1type UserName = string | null | undefined;
2type ValidUserName = NonNullable<UserName>;
3
4const userName: ValidUserName = "Alice"; // null and undefined cannot be chosen
5console.log(userName);
NonNullable<T>
excludesnull
andundefined
, ensuring that a value is present.
Conclusion
TypeScript utility types are powerful tools for making type definitions more concise and flexible. Understanding and appropriately using basic utility types like Partial
, Required
, Readonly
, Record
, etc., can enhance code reusability and maintainability. Mastering these types allows for more robust and safe type definitions, enabling efficient development.
TypeScript's keyof
Operator
TypeScript's keyof
operator is used to retrieve all property names of an object type. Using this operator, you can obtain the keys of an object type as a union type. This is extremely useful for writing type-safe code.
Basic Usage
1interface Person {
2 name: string;
3 age: number;
4 email: string;
5}
6
7// Use keyof to get the property names of Person
8type PersonKeys = keyof Person; // "name" | "age" | "email"
Example Usage
Using it in Function Arguments
You can use keyof
to define a function that has types based on specific properties of an object.
1function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
2 return obj[key];
3}
4
5const person: Person = { name: "Alice", age: 30, email: "alice@example.com" };
6const userName = getProperty(person, "name"); // type is string
7const age = getProperty(person, "age"); // type is number
8
9console.log("Name:", userName);
10console.log("Age:", age);
- This function retrieves a property from an object in a type-safe way based on the specified key.
Enhancing Type Constraints
By using keyof
, you can ensure that the keys passed to a function are validated at compile time.
1// Passing an invalid property name results in an error
2const invalid = getProperty(person, "invalidKey"); // Error
- This code demonstrates that specifying a non-existent property name will result in a compile-time error.
Summary
- The
keyof
operator is used to retrieve all the property names of an object type. - You can obtain the property names as a union type, achieving type-safe code.
- By using it in function arguments, you can restrict the function to accept only valid property names.
In this way, the keyof
operator enhances type safety in TypeScript and helps you write more robust code.
You can follow along with the above article using Visual Studio Code on our YouTube channel. Please also check out the YouTube channel.