DEV Community

Cover image for Typescript: Uniones discriminadas o como crear argumentos opcionales y dependientes
Matías Hernández Arellano
Matías Hernández Arellano

Posted on • Originally published at matiashernandez.dev

1

Typescript: Uniones discriminadas o como crear argumentos opcionales y dependientes

Es común tener componentes o funciones que aceptan argumentos que dependen entre sí, dónde una puede estar presente y la otra no.

¿Sabías que con Typescript puedes asegurarte de que este comportamiento se cumpla correctamente?

Veamos una situación imaginaria usando React

Tienes un componente que representa un gato, y este gato puede estar vivo o muerto y este comportamiento o estado está definido por las props que recibe.

function Cat({ isAlive, isDead}) {
  if(isAlive != null && isDead != null) {
    throw 'Ambas props no pueden estar definidas'
  } 
  let str = isAlive ? 'vivo' : 'muerto'

  return <>Este gato esta {str}</>
}


<Cat isAlive isDead />
Enter fullscreen mode Exit fullscreen mode

Pero evidentemente este gato no puede estar en ambos estados a la vez, si no, sería un gato the Schrödinger en vez de un gato real.

Además, el significado de ambas prop isAlive y isDead es similar pero contrario, por lo que para asegurar el correcto funcionamiento tendrías que escribir bloques condicionales que revisen las props y darle prioridad a una sobre otra.

¿Cómo puedes representar esto con typescript y por qué lo harías?

La idea de representar este comportamiento con Typescript es que el equívoco uso de las props sea detectado antes de que el código sea ejecutado y sin necesidad de escribir extra lógica para revisar el contenido de las props.

Para lograrlo harás uso de una combinación de propiedades opcionales, el tipo never y uniones.

/*
* Props de un gato vivo
* Si isAlive está presente entonces isDead no debe estarlo 
*/
type LivingCat = { isAlive: boolean, isDead?: never }

/*
* Props de un gato muerto
* Si isDead está presente entonces isLive no debe estarlo 
*/
type DeadCat = { isAlive?: never, isDead: boolean }

/*
* Crea una unión de las tipos definidos
*/
type RealCatProps = LivingCat | DeadCat 

/*
* Solo una prop puede estar presente. 
* La validación de este comportamiento se realiza en tiempo de compilación
*/
const RealCat = ({ isAlive, isDead}: RealCatProps) => {
    const str = isAlive ? 'living' : 'dead'
    return <>this is a {str} real cat</>
}
Enter fullscreen mode Exit fullscreen mode

En éste código se definen dos tipos muy similare LivingCat y DeadCat la diferencia radica en que propiedad esta definida como never.

En el primer caso, isDead se marca como opcional y como never, es decir, nunca será utilizada o definida.

El tipo never es utilizado cuando estás seguro que "algo" jamás ocurrirá.

En el segundo caso, ocurre lo contrario. La propiedad isLiving está marcada como opcional y como never.

Finalmente, creas una unión de los tipos, creando el tipo RealCatProps y usarás dicho tipo para definir las props del component RealCat.

Este component RealCat podrá ser instanciado sólo con una de las props, pero jamás ambas.

const RealApp = () => {
    return <RealCat isAlive />
}

/*
* Esto falla en tiempo de compilación ya que ambas props están presentes
*/
const ReailFalingApp = () => {
    return <RealCat isAlive={false} isDead={false} />
}
Enter fullscreen mode Exit fullscreen mode

Te invito a jugar con el código en este ejemplo con React o en el playground de typescript

Footer Social Card.jpg
✉️ Únete a Micro-bytes 🐦 Sígueme en Twitter ❤️ Apoya mi trabajo

AWS Q Developer image

Your AI Code Assistant

Ask anything about your entire project, code and get answers and even architecture diagrams. Built to handle large projects, Amazon Q Developer works alongside you from idea to production code.

Start free in your IDE

Top comments (0)

nextjs tutorial video

📺 Youtube Tutorial Series

So you built a Next.js app, but you need a clear view of the entire operation flow to be able to identify performance bottlenecks before you launch. But how do you get started? Get the essentials on tracing for Next.js from @nikolovlazar in this video series 👀

Watch the Youtube series

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay