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
default
esperamos un productohighlight
con propiedadesname
yprice
. - Para el slot
list
enviamos 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.