Utility types для Vue

В экосистеме Vue.js utility types играют важную роль, особенно при использовании TypeScript. Они позволяют создавать гибкие, типобезопасные компоненты и облегчать работу с реактивными данными. Рассмотрим ключевые utility types, их применение и тонкости использования в реальных проектах на Vue 3.


Ref и Reactive

Ref<T> используется для хранения реактивного примитивного значения или объекта. Примитивы, такие как строки, числа и булевы значения, должны оборачиваться в ref, чтобы Vue мог отслеживать изменения:

import { ref } from 'vue';

const count = ref(0);
count.value++; // реактивное обновление

Reactive<T> применяется для создания реактивного объекта целиком:

import { reactive } from 'vue';

const state = reactive({
  user: { name: 'Alice', age: 25 },
  loggedIn: false
});

state.loggedIn = true; // Vue отследит изменение

Ключевое различие: ref подходит для примитивов и оберток, reactive — для комплексных объектов.


Readonly и DeepReadonly

Readonly<T> делает объект или его свойства только для чтения. Полезно при передаче состояния, которое не должно изменяться в дочерних компонентах:

import { readonly } from 'vue';

const state = reactive({ count: 0 });
const readonlyState = readonly(state);

readonlyState.count = 10; // ошибка компиляции

DeepReadonly<T> обеспечивает глубокую неизменяемость всех вложенных свойств, что особенно важно при работе с большими структурами данных.


Partial и Required

Partial<T> превращает все свойства интерфейса или типа в опциональные, что удобно при обновлении частей состояния:

interface User {
  name: string;
  age: number;
}

function updateUser(user: User, updates: Partial<User>) {
  Object.assign(user, updates);
}

updateUser({ name: 'Alice', age: 25 }, { age: 26 });

Required<T> наоборот, делает все свойства обязательными, гарантируя, что объект полностью соответствует интерфейсу.


Pick и Omit

Pick<T, K> выбирает определённые свойства из типа:

interface User {
  id: number;
  name: string;
  age: number;
}

type UserPreview = Pick<User, 'id' | 'name'>;

Omit<T, K> исключает свойства из типа, что удобно для создания новых интерфейсов на основе существующих:

type UserWithoutAge = Omit<User, 'age'>;

Эти utility types помогают управлять структурой данных без дублирования типов.


Extract и Exclude

Extract<T, U> оставляет только те типы из T, которые совместимы с U. Exclude<T, U> удаляет из T все типы, совместимые с U.

type T = string | number | boolean;

type OnlyStr ing = Extract<T, string>; // string
type NotBoolean = Exclude<T, boolean>; // string | number

В Vue это удобно при работе с ограниченными наборами значений, например, для пропсов компонентов.


ReturnType и Parameters

ReturnType<T> позволяет получить тип возвращаемого значения функции:

function getUser() {
  return { name: 'Alice', age: 25 };
}

type UserType = ReturnType<typeof getUser>;

Parameters<T> возвращает тип параметров функции в виде кортежа, что полезно для типизации коллбеков:

function setUser(name: string, age: number) {}
type SetUserParams = Parameters<typeof setUser>; // [string, number]

В контексте Vue это особенно полезно для composition API и фабрик функций.


NonNullable и Awaited

NonNullable<T> исключает null и undefined из типа:

type MaybeString = string | null | undefined;
type DefinitelyString = NonNullable<MaybeString>; // string

Awaited<T> извлекает тип результата промиса:

type Response = Promise<{ data: string }>;
type DataType = Awaited<Response>; // { data: string }

Эти utility types помогают избежать ошибок, связанных с неопределёнными или асинхронными значениями в компонентах Vue.


Интеграция utility types с Composition API

При работе с setup() часто требуется строгая типизация:

import { ref, computed } from 'vue';

interface Product {
  name: string;
  price: number;
  discount?: number;
}

const product = ref<Product>({ name: 'Book', price: 100 });

const finalPrice = computed(() => product.value.price * (1 - (product.value.discount ?? 0)));

Использование utility types позволяет точно описывать структуру реактивных данных, их изменение и вычисляемые свойства.


Советы по практике

  • Всегда использовать readonly для пропсов, чтобы предотвратить их изменение внутри компонентов.
  • Partial и Omit упрощают работу с формами и объектами обновления.
  • ReturnType и Parameters помогают избегать дублирования интерфейсов для функций внутри composables.
  • Сочетание Ref, Reactive и utility types позволяет полностью контролировать типизацию реактивного состояния.

Utility types в Vue.js создают мощный инструментарий для безопасного и предсказуемого управления состоянием и компонентами. Их использование значительно снижает вероятность ошибок, облегчает поддержку кода и делает проект более масштабируемым.