Типизация композаблов

Quasar — это мощный фреймворк для разработки интерфейсов на Vue.js, который обеспечивает богатый набор компонентов и утилит. Одним из ключевых аспектов современного Vue 3 является использование композиционного API, а с ним тесно связана концепция композаблов (composables). Композаблы позволяют инкапсулировать логику и повторно использовать её между компонентами. Типизация композаблов с помощью TypeScript делает приложения более надежными, предсказуемыми и удобными для масштабирования.


Основы типизации композаблов

Композабл — это функция, которая возвращает реактивные состояния, методы и вычисляемые свойства. Для обеспечения строгой типизации необходимо использовать встроенные утилиты TypeScript, такие как Ref, ComputedRef и generics.

Пример базового композабла с типами:

import { ref, computed, Ref, ComputedRef } from 'vue';

export function useCounter(initialValue: number = 0) {
  const count: Ref<number> = ref(initialValue);

  const double: ComputedRef<number> = computed(() => count.value * 2);

  function increment(amount: number = 1): void {
    count.value += amount;
  }

  function decrement(amount: number = 1): void {
    count.value -= amount;
  }

  return {
    count,
    double,
    increment,
    decrement
  };
}

Разбор типизации:

  • Ref<number> указывает, что переменная count реактивная и содержит число.
  • ComputedRef<number> для double гарантирует, что вычисляемое свойство также имеет числовой тип.
  • Аргументы функций increment и decrement строго типизированы как number, что предотвращает случайное использование строк или других типов.

Использование интерфейсов для структурированных состояний

Для более сложных композаблов рекомендуется использовать интерфейсы. Это особенно полезно, если возвращается объект с множеством свойств и методов.

interface CounterComposable {
  count: Ref<number>;
  double: ComputedRef<number>;
  increment: (amount?: number) => void;
  decrement: (amount?: number) => void;
}

export function useCounter(initialValue: number = 0): CounterComposable {
  const count: Ref<number> = ref(initialValue);
  const double: ComputedRef<number> = computed(() => count.value * 2);

  const increment = (amount: number = 1) => {
    count.value += amount;
  };

  const decrement = (amount: number = 1) => {
    count.value -= amount;
  };

  return { count, double, increment, decrement };
}

Преимущества интерфейсов:

  • Автодополнение методов в редакторе.
  • Четкая структура возвращаемого объекта.
  • Защита от случайного добавления или изменения свойств.

Типизация реактивных объектов с reactive

Quasar и Vue позволяют использовать reactive для создания реактивных объектов. Важно корректно типизировать их, чтобы избежать ошибок доступа к свойствам.

import { reactive, toRefs } from 'vue';

interface FormState {
  username: string;
  email: string;
  password: string;
}

export function useForm() {
  const state = reactive<FormState>({
    username: '',
    email: '',
    password: ''
  });

  function resetForm(): void {
    state.username = '';
    state.email = '';
    state.password = '';
  }

  return {
    ...toRefs(state),
    resetForm
  };
}

Ключевые моменты:

  • Использование интерфейса FormState задает строгую структуру.
  • toRefs позволяет разложить реактивный объект на отдельные Ref, сохраняя реактивность и типизацию.

Типизация параметров композаблов

Иногда композаблы принимают сложные объекты в качестве параметров. Для обеспечения строгой типизации можно использовать generics:

import { ref, Ref } from 'vue';

export function useFetch<T>(url: string) {
  const data: Ref<T | null> = ref(null);
  const loading: Ref<boolean> = ref(false);

  async function fetchData(): Promise<void> {
    loading.value = true;
    try {
      const response = await fetch(url);
      data.value = (await response.json()) as T;
    } finally {
      loading.value = false;
    }
  }

  return { data, loading, fetchData };
}

Особенности:

  • T позволяет композаблу быть универсальным и использоваться с любым типом данных.
  • Ref<T | null> обеспечивает реактивность с возможностью отсутствия данных.
  • Типизация результата запроса предотвращает ошибки при работе с полученными данными.

Комбинация композаблов с Quasar API

Quasar предоставляет множество утилит и сервисов (например, Notify, Dialog, Loading). Для строгой типизации важно использовать встроенные типы из @quasar/app и quasar:

import { Notify } from 'quasar';
import { Ref, ref } from 'vue';

export function useNotification() {
  const message: Ref<string> = ref('');

  function showSuccess(msg: string): void {
    message.value = msg;
    Notify.create({
      message: msg,
      type: 'positive'
    });
  }

  function showError(msg: string): void {
    message.value = msg;
    Notify.create({
      message: msg,
      type: 'negative'
    });
  }

  return { message, showSuccess, showError };
}

Типизация композаблов при работе с Quasar API обеспечивает:

  • Безопасное использование методов.
  • Автодополнение и проверку аргументов.
  • Снижение числа ошибок в UI логике.

Рекомендации по типизации

  • Всегда использовать Ref и ComputedRef для реактивных значений.
  • Применять интерфейсы или типы для сложных объектов.
  • Использовать generics для универсальных композаблов.
  • Комбинировать reactive и toRefs для удобной работы с реактивными состояниями.
  • Типизировать параметры функций и возвращаемые значения, чтобы предотвратить runtime ошибки.
  • Для работы с Quasar API использовать встроенные типы и документацию.

Типизация композаблов в Quasar позволяет создавать надежные, масштабируемые и легко поддерживаемые приложения. Строгие типы обеспечивают предсказуемость поведения компонентов и удобство разработки в команде, особенно при работе с крупными проектами.