Dos figuras empujando formas cúbicas y cilíndricas, simbolizando la comparación de procesos.

No preguntes qué pueden hacer por ti los sindicalistas; pregunta qué puedes hacer tú por ellos.

3 Minuto de lectura

Los tipos de unión son útiles cuando tienes una colección de clases que no actúan, pero que deben ser procesadas todas de la misma manera.

Como desarrollador de software en Swimlane, a veces tengo la oportunidad de aprender cosas nuevas que no solo nos benefician a mí y al resto del equipo, sino que también hacen que nuestros productos sean más efectivos. En un proyecto reciente, me encargaron agregar un registro de auditoría. El requisito era registrar cada cambio (por ejemplo, crear, actualizar, eliminar) en cualquiera de nuestras colecciones de bases de datos en una nueva colección, AuditLog, y etiquetar cada registro con la acción realizada. Bastante simple.

Sin embargo, las acciones de actualización planteaban una complicación: los distintos objetos del motor de flujo de trabajo se pueden actualizar de distintas maneras (por ejemplo, se pueden habilitar, deshabilitar o iniciar sesión; se pueden actualizar los complementos), y no quería saturar las funciones de actualización con lógica de registro. Necesitaba escribir una función que tomara como argumentos el estado anterior y posterior del documento que se estaba modificando. Luego, con base en los cambios en el documento, determinar la acción que los había modificado. Pero ¿de qué tipo deberían ser los argumentos anterior y posterior?

clase AuditLogsService { registro asíncrono (antes: ???, después: ???): Promesa { //... } }

Una solución habría sido usar una interfaz. Nuestros documentos no implementan una clase base ni una interfaz, así que esto parecía sencillo. Cree una interfaz BaseDocument y haga que las ocho clases de documento la implementen. Luego, se pueden escribir "antes" y "después" en la interfaz.

Interfaz AuditLogable {}

El problema con esa solución es que la interfaz carecería de sentido, ya que ninguna de las clases del documento comparte propiedades ni métodos. Bueno, eso no es del todo cierto: comparten un campo "id". Por lo tanto, estaría modificando ocho clases diferentes solo para implementar una interfaz con una sola propiedad y sin métodos.

interfaz AuditLogable { id: cadena; }

¿Qué tal si añado el método de registro a la interfaz y permito que cada tipo de documento defina su propio comportamiento de registro? Eso es una buena orientación a objetos, ¿no? Solo que ahora tengo que implementar ocho métodos de registro, cada uno con un comportamiento prácticamente similar. ¿Por qué no uso una clase base en lugar de una interfaz para evitar errores de registro? Porque el comportamiento de cada método de registro es solo... principalmente Similares. Los plugins, por ejemplo, son el único tipo de documento que no tiene una acción de "crear". En su lugar, la llamamos "instalar".

Me di cuenta de que estaba abordando este problema al revés. Los documentos no deberían registrar la auditoría, sino que deberían registrarla ellos mismos. De esta forma, podría escribirlo todo en una sola función sin implementar interfaces innecesarias ni abstracciones confusas.

Afortunadamente, Typescript tiene una forma sencilla de construir este patrón: Tipos de Unión. Con un tipo de unión, no necesito abstraer ninguna propiedad ni comportamiento. Puedo decir, en efecto:,

“El argumento 'antes' es uno de los tipos que se pueden registrar en la auditoría‘. tipo AuditLogable = Usuario | Complemento | Playbook; // … clase AuditLogsService { async log(antes: AuditLogable, después: AuditLogable): Promise { //... } }

Debido a que la información de tipo se pierde en el tiempo de ejecución, también necesito pasar un parámetro adicional que indique qué tipo dentro de la unión estoy pasando como argumentos antes y después.

enum SwimType = { usuario, complemento, libro de jugadas, // ... } clase AuditLogsService { registro asíncrono (antes: AuditLogable, después: AuditLogable, tipo: SwimType): Promise { //... } }

Ahora he implementado AuditLogging con una sola función y un nuevo tipo de unión. No es necesario modificar ninguno de los documentos registrados, por lo que añadir una nueva clase al registrador es tan sencillo como añadirla a AuditLogable y añadir cualquier comportamiento adicional necesario a la función de registro. Todo es fácil de probar y de entender.

Solicitar una demostración en vivo