Uno de los conceptos más abstractos de Vue.js son los slots con ámbito, también llamados scoped slots.
Si a esto le añadimos las complejidades de añadir un sistema de tipado como TypeScript, la dificultad puede aumentar.
Sin embargo, con la macro defineSlots de Vue 3.3>, añadir tipos a tus slots es más sencillo que nunca. Deja que te muestre como.
Usando defineSlots
El objetivo de la macro es mejorar la DX añadiendo sugerencias y validación de tipo en tu editor de código/IDE. defineSlots acepta un tipo literal como parámetro cuya propiedad es el nombre del slot y, como valor, una función que define las propiedades que el slot acepta.
Por ejemplo, imagina que tu slot actions ofrece acceso a una función llamada open:
defineSlots<{
actions(props: { open: () => void }): any; // Return type is currently ignored by the macro
}>();
Además, como extra, defineSlots retorna el objeto slots que obtendrías llamando a la macro useSlots.
Ejemplo práctico con defineSlots
Supongamos en tu componente AppProducts quieres ofrecer información de productos y quieres asignar sus tipos.
interface Product {
id: number
name: string;
price: number;
}
const slots = defineSlots<{
default(props: {
highlight: { // You can use literal types
name: string;
price: number;
}
}): void
list(props: {
products: Product[];
}): void
}>()
Como ves, estamos declarando los tipos de dos slots:
- En el slot
defaultesperamos un productohighlightcon propiedadesnameyprice. - Para el slot
listenviamos una lista deProduct.
Esto, tan sencillo, tiene un profundo efecto. Por una parte estamos validando de forma adecuada los tilos tipos el slot en el componente AppProducts en sí. Por otro lado, TypeScript inferirá los tipos del slot en el componente “padre” que haga uso de los slots.
<template>
<AppProduct>
<template #default="{ highlight }">
<h2>{{ highlight.name }} - ${{ highlight.price }}</h2>
</template>
<template #list="{ products }">
<ul>
<li v-for="product in products" :key="product.id">
{{ product.name }} - ${{ product.price }}
</li>
</ul>
</template>
</AppProduct>
</template>
Demo
Como siempre, te dejo una demo.
