ProgramaciónArquitecto de TypeScript

¿Cómo se implementa el emparejamiento de patrones (pattern matching) en TypeScript a través de uniones discriminadas? ¿Cómo estructurar correctamente los tipos y qué trampas existen?

Supere entrevistas con el asistente de IA Hintsage

Respuesta

En TypeScript, el emparejamiento de patrones se implementa a través de "uniones discriminadas". A cada objeto en la unión se le asigna un campo-discriminador obligatorio (generalmente una cadena, por ejemplo type), por el cual TypeScript diferencia las variantes.

Ejemplo:

type Success = { type: 'success'; data: string }; type Failure = { type: 'failure'; error: string }; type Result = Success | Failure; function handleResult(result: Result) { switch (result.type) { case 'success': // result: Success console.log(result.data); break; case 'failure': // result: Failure console.error(result.error); break; } }

En el switch/case o if por el campo-discriminador, TypeScript "reducirá" el tipo exactamente a la variante necesaria.

Principales ventajas:

  • Tipado estricto — no se puede acceder a un campo inexistente.
  • Verificación de exhaustividad — si no se manejan todas las variantes, a veces se produce un error (se puede forzar explícitamente).

Pregunta trampa

Si se añade una nueva variante a la unión discriminada, ¿demandará TypeScript obligatoriamente actualizar todos los switch-case para manejar la nueva variante?

Respuesta: No, solo si se añade explícitamente el manejo de la variante "imposible". Por ejemplo, utilizando la función never:

Ejemplo:

function assertNever(x: never): never { throw new Error('Variant unexpected: ' + x); } function handle(r: Result) { switch(r.type) { case 'success': /* ... */; break; case 'failure': /* ... */; break; default: return assertNever(r); // TS mostrará un error si aparece un nuevo tipo } }

Ejemplos de errores reales debido a la falta de conocimiento sobre los matices del tema.


Historia

Después de extender el tipo "Result" con una nueva variante ('pending'), en varios lugares de la aplicación, los viejos switch-case no manejaron este caso. Como resultado, parte de las interfaces dejaron de funcionar. El error solo fue notado en producción una semana después del lanzamiento.


Historia

Intentar usar una unión discriminada sin un discriminador único (el campo type se duplicaba en dos tipos) llevó a la "dilución" de tipos: TypeScript dejó de restringir el tipo con precisión, y fue posible acceder a campos inexistentes sin un error de compilación. Varios errores críticos se fueron a producción.


Historia

En un proyecto, se implementó el emparejamiento de patrones a través de if-else en varios campos en lugar de utilizar un único discriminador explícito. Esto complicó la transición a la verificación de exhaustividad con la función never y dificultó la legibilidad del código: los switch-case no funcionaban correctamente y las nuevas variantes "rompían" la lógica existente.