Skip to content
← Todas las preguntas
Intermedio

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

TypeScriptComponentes

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>

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>

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>

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>

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>

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.

Publicado bajo la licencia MIT.