A medida que Typescript crece y gana popularidad recientemente, cada vez más desarrolladores de JavaScript valoran la seguridad de tipos. La lista de características que ofrece Typescript es enorme y puede resultar abrumadora, por lo que en esta publicación me centraré en una de ellas que es fácil de entender y tiene un impacto práctico muy claro.
Comencemos con un ejemplo. Imaginemos que estamos desarrollando una aplicación con muchos roles de usuario. Es bastante común que una aplicación sea utilizada por diferentes usuarios, ¿no es así? Los roles exactos no son realmente importantes aquí, pero digamos que son admin
, consumer
y guest
. En TypeScript, podemos declarar usuarios que tengan esos roles de la siguiente manera:
type Admin = {} type Consumer = {} type Guest = {}
Ahora, consideremos un conjunto de atributos que tiene cada rol de usuario. Por lo general, son email
, firstName
y lastName
o algo así. Pero, espere, los usuarios Guest
probablemente no los tengan (después de todo, son invitados), así que dejemos este tipo vacío por ahora.
type Admin = { firstName: string lastName: string email: string } type Consumer = { firstName: string lastName: string email: string } type Guest = {}
El usuario de una aplicación solo puede tener un rol. La forma de representar esto a través de tipos es utilizar un tipo union
.
type User = Admin | Consumer | Guest
Los administradores son famosos por sus habilidades exclusivas y, en nuestra aplicación, pueden invitar a los consumidores. Agreguemos un campo que indique cuántas invitaciones puede enviar un administrador.
type Admin = { firstName: string lastName: string email: string numberOfInvitesLeft: number // <-- added }
Para hacer las cosas más interesantes y cercanas a una aplicación real, agreguemos una propiedad exclusiva para un tipo Consumer
.
type Consumer = { firstName: string lastName: string email: string premium: boolean // <-- added }
Este es un ejemplo muy simple y, en realidad, los usuarios podrían tener docenas de propiedades dispares, lo que complica considerablemente la base de código cuando necesita acceder a ciertas propiedades.
const doSomethingBasedOnRole = (user: User) => { // how do you check here that user is really an admin if (user) { // ...and do something with the `numberOfInvitesLeft` property? } }
Una opción es verificar la existencia del inmueble.
const doSomethingBasedOnRole = (user: User) => { if (user && user.numberOfInvitesLeft) { // safely access `numberOfInvitesLeft` property } }
Pero esta es una solución tediosa y no escalable. ¿Y qué hacer cuando `numberOfInvitesLeft` se convierte en una propiedad opcional?
Aquí es donde entran en juego los tipos de unión discriminados. Solo tenemos que poner un campo adicional en cada tipo de usuario que indique el rol.
type Admin = { firstName: string lastName: string email: string numberOfInvitesLeft: number role: "admin" // <-- added } type Consumer = { firstName: string lastName: string email: string role: "consumer" // <-- added } type Guest = { role: "guest" // <-- added }
Observa cómo pongo una cadena específica como tipo; esto se llama tipo literal de cadena . Esto te permite usar operadores nativos del lenguaje JS, por ejemplo, switch case
, if
, else
para discriminar según el rol.
const user: Admin = { firstName: "John", lastName: "Smith", email: "[email protected]", numberOfInvitesLeft: 3, role: "admin", } const doSomethingBasedOnRole = (user: User) => { if (user.role === "admin") { // now typescript knows that INSIDE of this block user is of type `Admin` // now you can safely call `user.numberOfInvitesLeft` within this block } }
Lo mismo se aplica a una declaración de caso de cambio.
// ... const doSomethingBasedOnRole = (user: User) => { switch (user.role) { case "admin": { // now typescript knows that INSIDE of this block user is of type `Admin` // now you can safely call `user.numberOfInvitesLeft` within this block } case "consumer": { // do something with a `Consumer` user // if you try to call `user.numberOfInvitesLeft` here, TS compiler errors in // // "Property 'numberOfInvitesLeft' does not exist on type 'Consumer'." // } } }
Los beneficios de los tipos de unión discriminados son evidentes porque la verificación de tipos se basa en propiedades de rol explícitas y no en propiedades ad hoc que podrían o no estar relacionadas con un usuario específico.