Typescript: Uniones discriminadas o como crear argumentos opcionales y dependsientes

Es común tener componentes o funciones que aceptan argumentos que dependsen 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 Representativea 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 />


Pero evidenceemente 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는 esto con typescript y por qué lo harías를 대표합니까?



La Idea de Representative 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 logica para revisar el contenido de las props.

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

/*
* 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</>
}


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 프라이머 카소, 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. 라 프로피에다드 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 .

에스테틱 구성 요소 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} />
}


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




✉️ Únete a Micro-bytes 🐦 Sígueme en ❤️ Apoya mi trabajo

좋은 웹페이지 즐겨찾기