paint-brush
O poder das unións discriminadas en TypeScriptpor@tokenvolt
Nova historia

O poder das unións discriminadas en TypeScript

por Oleksandr Khrustalov4m2024/10/05
Read on Terminal Reader

Demasiado longo; Ler

Esta historia resume os conceptos básicos dos sindicatos discriminados en TypeScrips. Eu úsoos con bastante frecuencia no meu desenvolvemento, polo que veremos un exemplo concreto de como aplicalos.
featured image - O poder das unións discriminadas en TypeScript
Oleksandr Khrustalov HackerNoon profile picture

Enunciando o problema

A medida que mecanografía está crecendo e gañando popularidade recentemente, máis e máis desenvolvedores de javascript aprecian a seguridade do tipo. A lista de funcións que ofrece Typescript é enorme e pode ser abrumadora, polo que nesta publicación, centrareime nunha delas que é fácil de entender e que ten un bo impacto práctico.


Comecemos cun exemplo. Imaxina que estás a desenvolver unha aplicación con moitos roles de usuario. É bastante común que unha aplicación sexa consumida por diferentes usuarios, non si? Os roles exactos non son realmente importantes aquí, pero digamos que son admin , consumer e guest . No mecanografiado, podemos declarar os usuarios que teñen eses roles do seguinte xeito:


 type Admin = {} type Consumer = {} type Guest = {}


Agora, imos considerar un conxunto de atributos que ten cada rol de usuario. Normalmente, son email , firstName e lastName ou algo así. Pero, espera, probablemente os usuarios Guest non os teñan (a fin de contas son convidados), así que deixemos este tipo baleiro polo momento.


 type Admin = { firstName: string lastName: string email: string } type Consumer = { firstName: string lastName: string email: string } type Guest = {}


O usuario dunha aplicación só podería ter unha función. A forma de representar isto a través dos tipos é usar un tipo union .


 type User = Admin | Consumer | Guest


Os administradores son famosos polas súas habilidades exclusivas e, na nosa aplicación, poden convidar consumidores. Engademos un campo que indica cantas invitacións pode enviar un administrador.


 type Admin = { firstName: string lastName: string email: string numberOfInvitesLeft: number // <-- added }


Para facer as cousas máis interesantes e máis próximas a unha aplicación real, imos engadir unha propiedade exclusiva dun tipo Consumer .


 type Consumer = { firstName: string lastName: string email: string premium: boolean // <-- added }


Este é un exemplo moi sinxelo e, en realidade, os usuarios poderían ter ducias de propiedades dispares, o que complica considerablemente a base de código cando precisa acceder a determinadas 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? } }


Unha opción é comprobar a existencia do inmoble.


 const doSomethingBasedOnRole = (user: User) => { if (user && user.numberOfInvitesLeft) { // safely access `numberOfInvitesLeft` property } }


Pero esta é unha solución tediosa e non escalable. E que facer cando `numberOfInvitesLeft` se converte nunha propiedade opcional?

Presentación de tipos de sindicatos discriminados

Aquí é onde entran en xogo os tipos de sindicatos discriminados. Só necesitamos poñer un campo adicional en cada tipo de usuario indicando o 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 }


Observe como estou poñendo unha cadea específica como tipo; isto chámase tipo literal de cadea . O que che dá isto é que agora podes usar operadores nativos da linguaxe JS, por exemplo, switch case , if , else para discriminar o 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 } }


O mesmo aplícase a unha declaración switch case.


 // ... 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'." // } } }


Os beneficios dos tipos de unión discriminados son evidentes porque a comprobación de tipos baséase nunha propiedade de rol explícita e non en propiedades ad-hoc que poden ou non estar relacionadas cun usuario específico.