ProgramaciónDesarrollador Fullstack

¿Cómo funciona la compatibilidad estructural de tipos (Structural typing) en TypeScript? ¿En qué se diferencia de la tipificación nominal, cuáles son sus fortalezas y qué trampas se pueden encontrar?

Supere entrevistas con el asistente de IA Hintsage

Respuesta.

Historia de la pregunta

A diferencia de muchos lenguajes orientados a objetos, TypeScript implementa la tipificación estructural (duck typing): un objeto se considera compatible con un tipo si tiene todas las propiedades necesarias, independientemente de si se declara explícitamente como de ese tipo.

Problema

Esta flexibilidad a veces lleva a que se acepten objetos como tipos si la estructura coincide, aunque en realidad no estén relacionados en significado. Esto es peligroso en modelos de datos complejos, cuando la coincidencia de estructuras es accidental.

Solución

Siempre estructura correctamente los tipos de objetos, minimiza las coincidencias de estructuras entre diferentes entidades, y para casos críticos usa propiedades o símbolos adicionales para "nominalizar" los tipos.

Ejemplo de código:

interface Point { x: number; y: number; } interface Pixel { x: number; y: number; } function drawPoint(p: Point) { console.log(p.x, p.y); } const pixel: Pixel = { x: 1, y: 2 }; drawPoint(pixel); // OK, los tipos son estructuralmente compatibles

Características clave:

  • Verificación por estructura (propiedades y sus tipos), no por nombre de tipo
  • Facilidad de integración con el código JS existente
  • Posibles conflictos al coincidir estructuras de objetos con diferentes significados

Preguntas con trampa.

Si la estructura de dos interfaces coincide, ¿significa que son completamente intercambiables?

Puede que sí por estructura, pero no por la lógica del programa. Esto es aceptable a nivel de compilador, pero puede llevar a errores lógicos (por ejemplo, Point y Pixel arriba).

¿Se puede prohibir la compatibilidad estructural para algún tipo?

No completamente, pero se puede añadir una propiedad única (por ejemplo, con un símbolo):

interface Brand { _brand: unique symbol; }

Ahora otro objeto no podrá imitar este tipo sin el mismo símbolo único.

¿En qué se diferencia la tipificación estructural de la nominal?

La estructural se basa en la presencia de una estructura, la nominal en la coincidencia del nombre del tipo en un espacio de nombres determinado. En TS, por defecto, siempre se utiliza la tipificación estructural.

Errores comunes y anti-patrones

  • Aceptar erróneamente objetos no relacionados con la misma estructura
  • Coincidencia completa de firmas — imposibilidad de separar tipos conceptualmente diferentes
  • Falsa sensación de seguridad de tipos

Ejemplo de la vida real

Caso negativo

En varias entidades coincidieron accidentalmente los campos (por ejemplo, User y Admin como {id: number, name: string}), lo que llevó a confusiones al trabajar con contratos API.

Pros:

  • Menos código, más fácil de expandir a nuevas entidades

Contras:

  • Difícil de rastrear errores lógicos que no captura el compilador

Caso positivo

Uso de "etiquetas" únicas y campos no estándar para diferenciar tipos semánticamente diferentes con la misma estructura.

Pros:

  • Separación explícita por lógica de negocio
  • Mayor seguridad y claridad de conceptos

Contras:

  • Complejidad adicional en el código
  • Requiere una planificación más cuidadosa de los tipos