Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Як написати узагальнений (generic) компонент у Vue?

Компоненти на кшталт таблиці, списку з вибором чи автодоповнення працюють з будь-яким типом елементів. Без узагальнень доводиться писати items: any[], і батько втрачає перевірку типів у слотах і подіях.

Атрибут generic на <script setup>:

<!-- DataTable.vue -->
<script setup lang="ts" generic="T extends { id: number | string }">
defineProps<{
  rows: T[]
  columns: Array<{ key: keyof T; label: string }>
}>()

const emit = defineEmits<{
  select: [row: T]
}>()
</script>

<template>
  <table>
    <tr v-for="row in rows" :key="row.id" @click="emit('select', row)">
      <td v-for="column in columns" :key="String(column.key)">
        <slot :name="String(column.key)" :row="row">{{ row[column.key] }}</slot>
      </td>
    </tr>
  </table>
</template>

Що отримує батько:

<DataTable
  :rows="orders"
  :columns="[{ key: 'total', label: 'Сума' }]"
  @select="(order) => open(order.number)"
/>

T виводиться з переданого rows як Order: key: 'totla' - помилка, у @select параметр має тип Order, а в слоті row - теж Order.

Можливості:

  • кілька параметрів і обмеження: generic="T extends Item, K extends keyof T";
  • імпортовані типи в обмеженнях;
  • типізовані слоти через defineSlots<{ default(props: { item: T }): any }>().

Обмеження й нюанси:

  • runtime не знає про T - узагальнення існують лише для TypeScript. Vue перевіряє props за базовим типом (Array), а не за T;
  • виведення типу йде з props. Якщо T не можна вивести з жодного prop, він стає обмеженням (unknown чи тип з extends);
  • перевірку в шаблоні батька дає лише vue-tsc / Vue - Official; без них узагальнення - просто документація;
  • ref на узагальнений компонент типізується складніше - звичайний InstanceType<typeof DataTable> не працює, бо компонент - функція з параметром типу. Для таких випадків використовують ComponentExposed з пакета vue-component-type-helpers.

Коли не варто: якщо компонент завжди працює з одним типом - узагальнення лише ускладнюють читання.

Докладніше в документації: script setup: generics

Перевір себе

20 випадкових питань за спробу, після завершення - розбір кожної помилки

Схожі питання