typescript - Vue 3/Nuxt 3 Scoped slot med generisk datatyp härledd från props


Jag vill implementera en karusellkomponent i Nuxt v3. Komponenten tar emot en array av objekt. Komponenten implementerar bara logiken, inte stilen eller strukturen.

Här är min komponent nu:

components/tdx/carousel.vue
<template>
  <div>
    <slot name="last"></slot>
    <div v-for="item in items">
      <slot
        name="item"
        v-bind="item"
      ></slot>
    </div>
    <slot name="next"></slot>
  </div>
</template>

<script setup lang="ts">
const props = defineProps({
  items: {
    type: [],
    required: true,
  },
  spotlight: {
    type: Number,
    default: 1,
    validator(value: number) {
      return value > 0;
    },
  },
});
</script>

Logiken i karusellen här är inte viktig.

I föräldrakomponenten kan jag sedan använda komponenten så här:

<template>
  <div class="container">
    <TdxCarousel :items="exampleArray">
      <template #item="{ title, description }">
        <p class="font-semibold text-2xl">{{ title }}</p>
        <hr />
        <p>{{ description }}</p>
      </template>
    </TdxCarousel>
  </div>
</template>

<script setup lang="ts">
const exampleArray = ref([
  {
    title: 'Item 1',
    description: 'Desc of item 1',
  },
  {
    title: 'Item 2',
    description: 'Desc of item 2',
  },
]);
</script>

Detta fungerar bra. Vad jag vill ha utöver detta är typer. Typerna för title och description är förstås any eftersom i propsen för carousel.vue är typen på objekten unknown[].

Jag hittade den här artikeln som visar hur man gör en generisk komponent men jag vill inte ha detta eftersom jag skulle behöva krångla med automatiska importsystemet från Nuxt.

Hur kan jag uppnå typinferens från de givna objekten i carousel.vue-propsen?

Svar #1

Uppdatering: maj 2023

Från och med Vue 3.3 stöds generiska komponenter officiellt.

Du behöver definiera en generisk parameter. Ändra din carousel.vue-komponent för att använda generic-attributet i <script setup>-taggen, samt konvertera den för att använda typbaserad metod för defineProps för att den ska kunna identifiera den generiska korrekt.

<script setup lang="ts" generic="T extends any">
withDefaults(
  defineProps<{ items: T[]; spotlight?: number }>(), {
  spotlight: 1,
});
</script>
<template>
  <div>
    <slot name="last"></slot>
    <div v-for="item in items">
      <slot
        name="item"
        v-bind="item">
      </slot>
    </div>
    <slot name="next"></slot>
  </div>
</template>

Nu kommer egenskaperna på slotten korrekt att härledas från typen av objekt.

Innan maj 2023

  • RFC #436

I tidigare versioner av VSCode/Volar behövde du aktivera en experimentell flagga. Det krävde att du aktiverade experimentalRfc436-alternativet i din tsconfig.json under vueCompilerOptions.

// tsconfig.json
{
  // ...
  "vueCompilerOptions": {
    "experimentalRfc436": true
  }
}

Detta är inte längre nödvändigt eftersom det är aktiverat som standard i nyare versioner av Volar.

Relaterade artiklar