Типизация компонентов

Типизация компонентов является важной частью разработки крупных приложений на Vue.js. Она позволяет повысить надёжность кода, снизить количество ошибок и улучшить поддержку автодополнения в IDE. В современных версиях Vue.js типизация достигается как с помощью встроенных возможностей фреймворка, так и с использованием TypeScript.


Props и их типизация

Props — это входные параметры компонентов. Они могут иметь строгие типы, что помогает избежать передачи некорректных данных.

export default {
  props: {
    title: {
      type: String,
      required: true
    },
    count: {
      type: Number,
      default: 0
    },
    isActive: Boolean
  }
}

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

  • type определяет допустимый тип пропса (String, Number, Boolean, Array, Object, Function, Symbol).
  • required указывает на обязательность пропса.
  • default задаёт значение по умолчанию.
  • Для сложных типов можно использовать кастомные проверки через validator:
props: {
  age: {
    type: Number,
    validator: value => value >= 0 && value <= 120
  }
}

В TypeScript можно дополнительно указать тип пропса через интерфейсы:

interface UserProps {
  name: string;
  age?: number;
}

export default defineComponent({
  props: {
    name: String,
    age: Number
  } as PropType<UserProps>
});

Типизация событий

События компонентов (emit) также поддаются типизации. В Vue 3 используется функция defineEmits:

const emit = defineEmits<{
  (e: 'update', value: string): void;
  (e: 'delete', id: number): void;
}>();

emit('update', 'новое значение');
emit('delete', 123);

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

  • Первый параметр — название события.
  • Последующие параметры — аргументы события.
  • TypeScript гарантирует, что вызов emit соответствует объявленным типам.

Типизация слотов

Слоты в Vue.js позволяют передавать в компонент разметку с пользовательскими данными. Типизация слотов обеспечивает контроль передаваемых свойств:

const slots = defineSlots<{
  default: { message: string };
  header: {};
}>();

Использование типизированного слота в шаблоне:

<template #default="{ message }">
  <p>{{ message }}</p>
</template>
  • default и header — имена слотов.
  • { message: string } — набор доступных данных для слота.

Типизация реактивных данных

Компоненты Vue.js активно используют реактивные состояния. В TypeScript для типизации реактивных данных применяются ref и reactive:

import { ref, reactive } from 'vue';

const count = ref<number>(0);

const user = reactive<{
  name: string;
  age: number;
}>({
  name: 'Иван',
  age: 25
});

Важные моменты:

  • ref<Type>(initialValue) создаёт реактивное примитивное значение.
  • reactive<Type>(initialObject) создаёт реактивный объект с указанными типами свойств.
  • TypeScript проверяет соответствие присваиваемых значений типам.

Использование defineComponent с типами

Для больших компонентов удобно использовать функцию defineComponent, которая объединяет props, emits, slots и setup с полной поддержкой TypeScript:

import { defineComponent, PropType } from 'vue';

export default defineComponent({
  props: {
    title: {
      type: String as PropType<string>,
      required: true
    },
    tags: {
      type: Array as PropType<string[]>,
      default: () => []
    }
  },
  emits: ['save', 'cancel'],
  setup(props, { emit }) {
    const handleSave = () => emit('save');
    const handleCancel = () => emit('cancel');
    return { handleSave, handleCancel };
  }
});
  • Применение PropType<T> позволяет точно указывать сложные типы пропсов.
  • Полная интеграция с IDE обеспечивает автодополнение и проверку типов.

Типизация глобальных компонентов

Vue.js поддерживает регистрацию компонентов с типизацией для глобального использования. Это позволяет избежать ошибок при динамическом рендеринге:

declare module '@vue/runtime-core' {
  export interface GlobalComponents {
    MyButton: typeof import('./components/MyButton.vue').default;
  }
}

После объявления TypeScript понимает, что <MyButton /> доступен во всех шаблонах без явного импорта.


Типизация composables

Компоненты часто используют composables для логики состояния. Типизация таких функций обеспечивает строгий контроль:

function useCounter(initialValue: number) {
  const count = ref(initialValue);
  const increment = () => count.value++;
  return { count, increment };
}

const { count, increment } = useCounter(0);
  • ref<number> гарантирует, что count.value всегда число.
  • TypeScript проверяет все операции с реактивным состоянием.

Интеграция с Vuex и Pinia

Типизация компонентов становится особенно важной при работе с глобальными состояниями:

Pinia:

import { defineStore } from 'pinia';

export const useUserStore = defineStore('user', {
  state: () => ({
    name: '' as string,
    age: 0 as number
  }),
  actions: {
    setName(name: string) {
      this.name = name;
    }
  }
});

Компонент:

const userStore = useUserStore();
userStore.setName('Иван');
  • Полная поддержка типов состояния и действий.
  • Ошибки присваивания или вызова методов обнаруживаются на этапе компиляции.

Типизация компонентов во Vue.js существенно повышает стабильность приложений и упрощает разработку крупных проектов. Она охватывает props, события, слоты, реактивные данные, composables и глобальные компоненты, а использование TypeScript делает проверку типов максимально строгой и безопасной.