Helpers

Utilities

Funzioni utility pure — extractErrorMessage (errori leggibili) e debounce (anti-chiamate eccessive).

API

// da @pzeta/mfe-contracts/utils
function extractErrorMessage(err: unknown, fallback?: string): string
function debounce<A extends unknown[]>(
  fn: (...args: A) => void,
  delay: number,
): (...args: A) => void

Utility pure usate sia internamente dai composables (useCrudResource, useCrudView) sia esposte per consumo diretto.

extractErrorMessage

Estrae un messaggio leggibile da errori di qualsiasi tipo, gestendo Error, stringhe, oggetti con campo message e unknown.

import { extractErrorMessage } from '@pzeta/mfe-contracts/utils'

try {
  await rischio()
} catch (err: unknown) {
  const msg = extractErrorMessage(err, 'Operazione fallita')
  console.error(msg)
}

Comportamento

InputOutput
Error (o sottoclasse)err.message
stringerr
{ message: string } (object con message stringa)err.message
null, undefined, numero, oggetto senza messagefallback (default 'Errore sconosciuto')
extractErrorMessage(new Error('Boom'))          // 'Boom'
extractErrorMessage('Errore manuale')           // 'Errore manuale'
extractErrorMessage({ message: 'API failure' }) // 'API failure'
extractErrorMessage({ status: 500 })            // 'Errore sconosciuto'
extractErrorMessage(null)                       // 'Errore sconosciuto'
extractErrorMessage(undefined, 'Custom')        // 'Custom'

Quando usarla

Sostituisce il pattern duplicato:

// Prima
const msg = err instanceof Error ? err.message : 'Fallback'

// Dopo
const msg = extractErrorMessage(err, 'Fallback')

Particolarmente utile in catch blocks dove err è tipato unknown (TypeScript strict).

Esempio con ApiError-like

// Backend errors spesso arrivano come { message, status, code, ... }
try {
  await ctx.http.post('/api/clienti', data)
} catch (err) {
  const userMessage = extractErrorMessage(err, 'Impossibile creare il cliente')
  toast.add({ severity: 'error', detail: userMessage, life: 5000 })
}

debounce

Crea una versione debounced type-safe di una funzione: la chiamata effettiva avviene solo dopo delay ms senza nuove invocazioni.

import { debounce } from '@pzeta/mfe-contracts/utils'

const debouncedSearch = debounce((query: string) => {
  fetchResults(query)
}, 300)

debouncedSearch('a')   // attende
debouncedSearch('ab')  // resetta timer
debouncedSearch('abc') // resetta timer
// dopo 300ms di inattività → fetchResults('abc')

Generic type-safe

Il parametro A è inferito dalla firma di fn:

// Type-checked: accetta solo argomenti coerenti con la firma originale
debouncedSearch('ok')   // ✓
debouncedSearch(123)    // ✗ Argument of type 'number' is not assignable to type 'string'

Quando usarla

  • Filtraggio input testuale (search bar, autocomplete)
  • Validazione di form al digitare
  • Resize handler della finestra
  • Save automatico di un form ogni X ms di pausa
<script setup lang="ts">
import { ref, watch } from 'vue'
import { debounce } from '@pzeta/mfe-contracts/utils'

const query = ref('')
const results = ref<Cliente[]>([])

const search = debounce(async (q: string) => {
  if (q.length < 3) return
  results.value = (await ctx.http.get<Cliente[]>('/api/clienti', { params: { q } })).data
}, 300)

watch(query, (q) => search(q))
</script>

<template>
  <input v-model="query" placeholder="Cerca cliente..." />
  <ul>
    <li v-for="c in results" :key="c.id">{{ c.ragioneSociale }}</li>
  </ul>
</template>

Cleanup

L'implementazione interna usa setTimeout. Non c'è API esplicita per cancellare un timer pendente. Se il componente si smonta mentre c'è un timer in volo:

  • la callback verrà chiamata comunque dopo delay
  • se accede a stato reattivo già pulito, ottieni un comportamento indesiderato

Per cleanup esplicito, gestiscilo a livello applicativo:

import { onBeforeUnmount, ref } from 'vue'

let isUnmounted = false
onBeforeUnmount(() => { isUnmounted = true })

const search = debounce((q: string) => {
  if (isUnmounted) return
  fetchResults(q)
}, 300)

In alternativa, usa useDebounceFn di @vueuse/core che gestisce il cleanup automaticamente.

Quando preferire alternative

CasoUsa
Debounce con cleanup automaticouseDebounceFn di @vueuse/core
Throttle (rate-limit, non debounce)useThrottleFn di @vueuse/core
Error message con stack trace o dettagli HTTPWrapper custom + extractErrorMessage come fallback

Tipi correlati