TypeScript предоставляет набор утилитных типов, которые упрощают выполнение распространенных преобразований типов. Эти утилиты позволяют разработчикам создавать новые типы на основе существующих, без необходимости дублирования кода.
Введение в утилитные типы
Для начала рассмотрим пример использования утилитных типов. Допустим, у нас есть тип Todo, который представляет собой объект с несколькими свойствами:
type Todo = {
readonly id: number;
title: string;
description?: string;
category?: string;
status: "done" | "in-progress" | "todo";
readonly createdAtTimestamp: number;
updatedAtTimestamp: number | null;
};
Если мы хотим создать новый тип TodoPreview, который содержит только свойства title и status, мы можем использовать утилитный тип Pick:
type TodoPreview = Pick<Todo, "title" | "status">;
Это позволит нам создать новый тип TodoPreview без необходимости дублирования кода.
Преимущества использования утилитных типов
Использование утилитных типов имеет несколько преимуществ. Во-первых, они позволяют сделать код более читаемым и понятным. Когда мы используем утилитные типы, мы явно указываем, как новый тип связан с существующим. Это делает код более самодокументирующимся и легким для понимания.
Во-вторых, утилитные типы позволяют избежать дублирования кода. Когда мы используем утилитные типы, мы не дублируем существующий код, а вместо этого создаем новый тип на основе существующего. Это делает код более поддерживаемым и легким для изменения.
Проблемы дублирования кода
Дублирование кода может привести к проблемам поддержки и изменения кода. Когда мы дублируем код, мы создаем несколько мест, где необходимо производить изменения. Это может привести к ошибкам и несоответствиям.
Например, если мы имеем несколько типов, которые содержат одно и то же свойство, и мы хотим изменить это свойство, нам придется изменить все типы. Это может быть трудоемким и склонным к ошибкам процессом.
Утилитные типы и принцип единого источника истины
Утилитные типы позволяют следовать принципу единого источника истины (Single Source of Truth, SSOT). Этот принцип гласит, что должна быть только одна точка, где хранится информация, и все остальные места должны ссылаться на эту точку.
Используя утилитные типы, мы можем создать один источник истины для наших типов и затем ссылаться на него из других мест. Это делает код более поддерживаемым и легким для изменения.
Пример использования утилитных типов
Допустим, у нас есть несколько типов, которые содержат одно и то же свойство:
type Todo = {
readonly id: number;
title: string;
description?: string;
category?: string;
status: "done" | "in-progress" | "todo";
readonly createdAtTimestamp: number;
updatedAtTimestamp: number | null;
};
type TodoPreview = {
title: string;
status: "done" | "in-progress" | "todo";
};
type TodoUpdate = {
title?: string;
description?: string;
category?: string;
status?: "done" | "in-progress" | "todo";
updatedAtTimestamp?: number | null;
};
Если мы хотим изменить свойство status во всех типах, мы можем использовать утилитные типы, чтобы создать один источник истины для этого свойства:
type Status = "done" | "in-progress" | "todo";
type Todo = {
readonly id: number;
title: string;
description?: string;
category?: string;
status: Status;
readonly createdAtTimestamp: number;
updatedAtTimestamp: number | null;
};
type TodoPreview = Pick<Todo, "title" | "status">;
type TodoUpdate = {
title?: string;
description?: string;
category?: string;
status?: Status;
updatedAtTimestamp?: number | null;
};
Теперь, если мы хотим изменить свойство status, нам нужно изменить только один источник истины, и все остальные места будут автоматически обновлены.