Skip to content
← Todas las preguntas
⚡ Intermedio

¿Cómo funciona withDefaults y cuáles son sus errores habituales?

🔷 TypeScript🧩 Componentes

withDefaults proporciona valores por defecto para defineProps basado en tipos. El principal error: los defaults mutables (arrays, objetos) DEBEN usar funciones factory; de lo contrario, todas las instancias del componente comparten la misma referencia. Vue 3.5+ introduce defaults con desestructuración que gestionan esto automáticamente.

Uso básico

vue
<script setup lang="ts">
interface Props {
  title?: string
  count?: number
  items?: string[]
}

const props = withDefaults(defineProps<Props>(), {
  title: 'Untitled',
  count: 0,
  items: () => []
})
</script>
Open in Vue Playground

withDefaults solo se aplica a los props opcionales (los que tienen ?). Los props requeridos no necesitan defaults.

El problema del default mutable

Los arrays y objetos se pasan por referencia. Sin una función factory, cada instancia del componente obtiene el mismo objeto:

vue
<script setup lang="ts">
// ERROR: todas las instancias comparten el mismo array
const props = withDefaults(defineProps<{ tags?: string[] }>(), {
  tags: ['default']
})
</script>
Open in Vue Playground

Si una instancia del componente muta tags, todas las demás instancias ven el cambio. Esto causa bugs como "seleccionar una fila en una tabla la selecciona en todas las tablas".

La solución: funciones factory

Envuelve los defaults mutables en una función flecha para que cada instancia obtenga una copia nueva:

vue
<script setup lang="ts">
interface Props {
  title?: string
  disabled?: boolean
  items?: string[]
  config?: { theme: string; locale: string }
  selectedIds?: Set<string>
}

const props = withDefaults(defineProps<Props>(), {
  title: 'Default', // primitivo, no necesita factory
  disabled: false, // primitivo, no necesita factory
  items: () => [], // array, factory necesaria
  config: () => ({
    // objeto, factory necesaria
    theme: 'light',
    locale: 'en'
  }),
  selectedIds: () => new Set() // Set, factory necesaria
})
</script>
Open in Vue Playground

Cuándo necesitas una función factory

TipoFactory necesariaSintaxis del default
stringNo'hello'
numberNo42
booleanNofalse
nullNonull
Array() => []
Object() => ({})
Map / Set() => new Map()
Date() => new Date()

La regla: si typeof value === 'object', usa una factory.

Vue 3.5+: defaults con desestructuración

Vue 3.5 introdujo la desestructuración reactiva de props. Los defaults se escriben con la sintaxis de desestructuración estándar de JavaScript, y Vue gestiona automáticamente el aislamiento entre instancias:

vue
<script setup lang="ts">
const {
  title = 'Untitled',
  count = 0,
  items = ['default'],
  config = { theme: 'light' }
} = defineProps<{
  title?: string
  count?: number
  items?: string[]
  config?: { theme: string }
}>()
</script>
Open in Vue Playground

Sin withDefaults, sin funciones factory. Cada instancia del componente obtiene su propia copia. Este es el enfoque recomendado en Vue 3.5+.

withDefaults vs defaults con desestructuración

vue
<!-- Vue 3.4 e inferior: withDefaults -->
<script setup lang="ts">
const props = withDefaults(
  defineProps<{
    items?: string[]
    label?: string
  }>(),
  {
    items: () => [],
    label: 'Default'
  }
)
// acceso: props.items, props.label
</script>

<!-- Vue 3.5+: defaults con desestructuración -->
<script setup lang="ts">
const { items = [], label = 'Default' } = defineProps<{
  items?: string[]
  label?: string
}>()
// acceso: items, label (directamente, sin prefijo 'props.')
</script>
Open in Vue Playground

Ambos enfoques son válidos. withDefaults te da un único objeto props. La desestructuración te da variables individuales. En Vue 3.5+, la desestructuración es más simple y evita por completo el error habitual de las funciones factory.

Ver también: ¿Cómo declarar props con TypeScript? · ¿Cuáles son las limitaciones de los tipos en defineProps?

Referencias

Publicado bajo la licencia MIT.