Custom Utility Types in TypeScript (Live Playground)
In this tutorial, we will explore creating custom utility types in TypeScript. Understanding how to create custom utility types will help you write more flexible and type-safe code.
Custom utility types are user-defined generic types that can be used to perform common operations on types, similar to built-in utility types like Partial
and Readonly
.
Deep Readonly
The DeepReadonly
utility type creates a new type with all the properties of the original type set as readonly, recursively:
type DeepReadonly<T> = {
readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K];
};
interface Person {
name: string;
details: {
age: number;
address: string;
};
}
type ReadonlyPerson = DeepReadonly<Person>;
const readonlyPerson: ReadonlyPerson = {
name: 'John Doe',
details: { age: 30, address: '123 Main St' },
};
readonlyPerson.details.age = 31; // Error: Cannot assign to 'age' because it is a read-only property
Required Properties
The RequiredProperties
utility type creates a new type with the specified properties of the original type set as required:
type RequiredProperties<T, K extends keyof T> = Required<Pick<T, K>> & Omit<T, K>;
interface Person {
name: string;
age?: number;
address?: string;
}
type PersonWithName = RequiredProperties<Person, 'name'>;
const personWithName: PersonWithName = { name: 'John Doe' };
NonEmptyArray
The NonEmptyArray
utility type creates a new type that ensures an array has at least one element:
type NonEmptyArray<T> = [T, ...T[]];
const validArray: NonEmptyArray<number> = [1, 2, 3];
const invalidArray: NonEmptyArray<number> = []; // Error: Type '[]' is not assignable to type 'NonEmptyArray<number>'
Conclusion
In this tutorial, we have explored creating custom utility types in TypeScript. Understanding how to create custom utility types effectively will enable you to create more flexible and type-safe code.