ProgramaciónDesarrollador Frontend

¿Cómo tipar correctamente una función de retorno (callback) en TypeScript y qué trampas hay que tener en cuenta al trabajar con el contexto y los errores de tipo?

Supere entrevistas con el asistente de IA Hintsage

Respuesta.

Historia de la pregunta: En JavaScript, las funciones callback se utilizan en todas partes, pero sus firmas a menudo no son evidentes. En TypeScript, es importante especificar explícitamente los tipos de los parámetros y el valor de retorno, de lo contrario, es fácil tener agujeros en la seguridad de tipos.

Problema: Una tipificación incorrecta o laxa de los callbacks lleva a tipos indefinidos de argumentos y resultados, complica el trabajo con el contexto (this), rompe la verificación automática de errores por parte del compilador y dificulta la refactorización.

Solución: Es necesario definir explícitamente el tipo de la función callback, especificar los tipos de los parámetros pasados, manejar correctamente los argumentos opcionales y el valor de retorno, y si es necesario, fijar explícitamente el tipo del contexto.

Ejemplo de código:

type Callback = (error: Error | null, result?: string) => void; function doAsyncWork(data: string, cb: Callback): void { setTimeout(() => { if (data === '') cb(new Error('Cadena vacía')); else cb(null, data.toUpperCase()); }, 100); }

Características clave:

  • Siempre especifique los tipos de todos los parámetros del callback.
  • Describa el tipo de retorno, incluso si es void.
  • Si es necesario, fije explícitamente el tipo de this (por ejemplo, a través de una función con contexto).

Preguntas capciosas.

¿Qué sucede si no se especifica el tipo de retorno del callback?

TypeScript aceptará cualquier tipo de retorno (por ejemplo, undefined, void, Promise), lo que puede llevar a sorpresas en cadenas asíncronas o al devolver valores "por defecto".

type BadCallback = (data: string) => any; // cualquier resultado, falta de control

¿Se puede escribir callback como Function o (...args: any[]) => any?

No se puede. Esto elimina toda la protección de tipos, se pierde información sobre la cantidad de parámetros, sus tipos y el tipo de retorno. Este enfoque resulta más costoso que renunciar completamente a TypeScript.

¿Cómo tipar una función con contexto this?

Utilice el primer parámetro this en la firma de la función o haga un casting a través de bind. Por ejemplo:

interface ClickCallback { (this: HTMLElement, event: MouseEvent): void; } const handler: ClickCallback = function (event) { this.textContent = 'ok'; };

Errores de tipo y anti-patrones

  • Callbacks sin tipificar (any o Function)
  • Ausencia de tipo de retorno en la firma de la función
  • Una discrepancia en el tipo de this conduce a errores de runtime aleatorios

Ejemplo de la vida real

Caso negativo

En un proyecto, el callback fue declarado como (...args: any[]) => any. Al actualizar la lógica de negocio, la firma cambió, el callback dejó de pasar los argumentos necesarios, los errores solo aparecieron en producción.

Ventajas:

  • Más fácil compilar e integrar código de terceros

Desventajas:

  • Sin protección a nivel de tipos
  • Dificultades en la actualización

Caso positivo

Se implementaron tipos estrictos: se describieron las interfaces de los callbacks, se comenzó a especificar explícitamente el tipo de this y el tipo de retorno. El compilador comenzó a capturar errores antes del despliegue, se simplificó la refactorización y el soporte para arreglos de errores.

Ventajas:

  • Seguridad de tipos
  • Verificación de cambios en las firmas en tiempo de compilación

Desventajas:

  • La tipificación se volvió un poco más complicada, aumentó el volumen de boilerplate