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 />
sponsor
Tu producto o servicio podría estar aquí
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</>
}
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.
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} />
}
Te invito a jugar con el código en este ejemplo con React o en el playground de typescript
😃 Thanks for reading!
Did you like the content? Found more content like this by joining to the Newsletter or following me on Twitter