Raziščite zmogljive TypeScript alternative enumom: const assertions in union types. Naučite se, kdaj uporabiti katero za robustno in vzdržljivo kodo.
Onkraj Enumov: TypeScript Const Assertions proti Union Types
V svetu statično tipiziranega JavaScripta s TypeScriptom so bili enumi dolgo časa prva izbira za predstavitev fiksnega nabora poimenovanih konstant. Ponujajo jasen in berljiv način za definiranje zbirke povezanih vrednosti. Vendar pa, ko projekti rastejo in se razvijajo, razvijalci pogosto iščejo bolj prilagodljive in včasih učinkovitejše alternative. Dva močna tekmeca, ki se pogosto pojavljata, sta const assertions in union types. Ta objava se poglablja v nianse uporabe teh alternativ tradicionalnim enumom, ponuja praktične primere in vas vodi pri izbiri, katero izbrati.
Razumevanje Tradicionalnih TypeScript Enumov
Preden raziščemo alternative, je bistveno, da dobro razumemo, kako delujejo standardni TypeScript enumi. Enumi vam omogočajo, da definirate niz poimenovanih številskih ali nizovnih konstant. Lahko so številski (privzeto) ali pa temeljijo na nizih.
Številski Enumi
Privzeto so članom enum dodeljene številske vrednosti, začenši z 0.
enum DirectionNumeric {
Up,
Down,
Left,
Right
}
let myDirection: DirectionNumeric = DirectionNumeric.Up;
console.log(myDirection); // Output: 0
Lahko tudi izrecno dodelite številske vrednosti.
enum StatusCode {
Success = 200,
NotFound = 404,
InternalError = 500
}
let responseStatus: StatusCode = StatusCode.Success;
console.log(responseStatus); // Output: 200
Nizovni Enumi
Nizovni enumi so pogosto bolj priljubljeni zaradi izboljšane izkušnje pri odpravljanju napak, saj so imena članov ohranjena v prevedenem JavaScriptu.
enum ColorString {
Red = "RED",
Green = "GREEN",
Blue = "BLUE"
}
let favoriteColor: ColorString = ColorString.Blue;
console.log(favoriteColor); // Output: "BLUE"
Obremenitev Enumov
Čeprav so enumi priročni, imajo majhno obremenitev. Ko so prevedeni v JavaScript, se TypeScript enumi spremenijo v objekte, ki imajo pogosto obratne preslikave (npr. preslikava številske vrednosti nazaj na ime enum). To je lahko koristno, vendar prispeva tudi k velikosti paketa in morda ni vedno potrebno.
Razmislite o tem preprostem nizovnem enumu:
enum Status {
Pending = "PENDING",
Processing = "PROCESSING",
Completed = "COMPLETED"
}
V JavaScriptu bi to lahko postalo nekaj takega:
var Status;
(function (Status) {
Status["Pending"] = "PENDING";
Status["Processing"] = "PROCESSING";
Status["Completed"] = "COMPLETED";
})(Status || (Status = {}));
Za preproste, samo za branje nabore konstant se ta ustvarjena koda lahko zdi nekoliko pretirana.
Alternativa 1: Const Assertions
Const assertions so močna funkcija TypeScripta, ki vam omogoča, da prevajalniku poveste, naj za vrednost sklepa najbolj specifičen možen tip. Ko se uporabljajo z nizami ali objekti, ki naj bi predstavljali fiksni nabor vrednosti, lahko služijo kot lahka alternativa enumom.
Const Assertions z Nizami
Lahko ustvarite niz nizovnih literalov in nato uporabite const assertion, da bo njegov tip nespremenljiv, elementi pa literalni tipi.
const statusArray = ["PENDING", "PROCESSING", "COMPLETED"] as const;
type StatusType = typeof statusArray[number];
let currentStatus: StatusType = "PROCESSING";
// currentStatus = "FAILED"; // Error: Type '"FAILED"' is not assignable to type 'StatusType'.
function processStatus(status: StatusType) {
console.log(`Processing status: ${status}`);
}
processStatus("COMPLETED");
Razčlenimo, kaj se tukaj dogaja:
as const: Ta assertion pove TypeScriptu, naj obravnava niz kot samo za branje in sklepa najbolj specifične literalne tipe za njegove elemente. Torej, namesto `string[]` postane tip `readonly ["PENDING", "PROCESSING", "COMPLETED"]`.typeof statusArray[number]: To je preslikan tip. Iterira po vseh indeksihstatusArrayin izvleče njihove literalne tipe. Indeksna signaturanumberv bistvu pravi "daj mi tip katerega koli elementa v tem nizu." Rezultat je union tip:"PENDING" | "PROCESSING" | "COMPLETED".
Ta pristop zagotavlja varnost tipov, podobno kot nizovni enumi, vendar ustvari minimalni JavaScript. statusArray sam ostane niz nizov v JavaScriptu.
Const Assertions z Objekti
Const assertions so še močnejše, če jih uporabimo na objektih. Lahko definirate objekt, kjer ključi predstavljajo vaše poimenovane konstante, vrednosti pa so literalni nizi ali števila.
const userRoles = {
Admin: "ADMIN",
Editor: "EDITOR",
Viewer: "VIEWER"
} as const;
type UserRole = typeof userRoles[keyof typeof userRoles];
let currentUserRole: UserRole = "EDITOR";
// currentUserRole = "GUEST"; // Error: Type '"GUEST"' is not assignable to type 'UserRole'.
function displayRole(role: UserRole) {
console.log(`User role is: ${role}`);
}
displayRole(userRoles.Admin); // Valid
displayRole("EDITOR"); // Valid
V tem primeru objekta:
as const: Ta assertion naredi celoten objekt samo za branje. Še pomembneje pa je, da sklepa literalne tipe za vse vrednosti lastnosti (npr."ADMIN"namestostring) in naredi lastnosti same samo za branje.keyof typeof userRoles: Ta izraz rezultira v union ključev objektauserRoles, ki je"Admin" | "Editor" | "Viewer".typeof userRoles[keyof typeof userRoles]: To je iskalni tip. Vzame union ključev in ga uporabi za iskanje ustreznih vrednosti v tipuuserRoles. To rezultira v union vrednosti:"ADMIN" | "EDITOR" | "VIEWER", kar je naš želeni tip za vloge.
Izhod JavaScript za userRoles bo navaden JavaScript objekt:
var userRoles = {
Admin: "ADMIN",
Editor: "EDITOR",
Viewer: "VIEWER"
};
To je precej lažje od tipičnega enum.
Kdaj Uporabiti Const Assertions
- Konstante samo za branje: Ko potrebujete fiksni niz nizovnih ali številskih literalov, ki se ne smejo spreminjati med izvajanjem.
- Minimalni izhod JavaScript: Če vas skrbi velikost paketa in želite najbolj učinkovito izvajalno predstavitev za svoje konstante.
- Struktura, podobna objektu: Ko imate raje berljivost parov ključ-vrednost, podobno kot bi strukturirali podatke ali konfiguracijo.
- Nizovi, ki temeljijo na nizih: Posebej uporabno za predstavljanje stanj, tipov ali kategorij, ki jih je najbolje prepoznati po opisnih nizih.
Alternativa 2: Union Types
Union types vam omogočajo, da deklarirate, da lahko spremenljivka vsebuje vrednost enega od več tipov. V kombinaciji z literalnimi tipi (nizovni, številski, logični literali) tvorijo močan način za definiranje nabora dovoljenih vrednosti brez potrebe po izrecni deklaraciji konstante za sam nabor.
Union Types z Nizovnimi Literali
Lahko neposredno definirate union nizovnih literalov.
type TrafficLightColor = "RED" | "YELLOW" | "GREEN";
let currentLight: TrafficLightColor = "YELLOW";
// currentLight = "BLUE"; // Error: Type '"BLUE"' is not assignable to type 'TrafficLightColor'.
function changeLight(color: TrafficLightColor) {
console.log(`Changing light to: ${color}`);
}
changeLight("RED");
// changeLight("REDDY"); // Error
To je najbolj neposreden in pogosto najbolj jedrnat način za definiranje nabora dovoljenih nizovnih vrednosti.
Union Types s Številskimi Literali
Podobno lahko uporabite številske literale.
type HttpStatusCode = 200 | 400 | 404 | 500;
let responseCode: HttpStatusCode = 404;
// responseCode = 201; // Error: Type '201' is not assignable to type 'HttpStatusCode'.
function handleResponse(code: HttpStatusCode) {
if (code === 200) {
console.log("Success!");
} else {
console.log(`Error code: ${code}`);
}
}
handleResponse(500);
Kdaj Uporabiti Union Types
- Preprosti, neposredni nabori: Ko je nabor dovoljenih vrednosti majhen, jasen in ne zahteva opisnih ključev poleg samih vrednosti.
- Implicitne konstante: Ko vam ni treba sklicevati na poimenovano konstanto za sam nabor, ampak raje neposredno uporabite literalne vrednosti.
- Največja jedrnatost: Za enostavne scenarije, kjer se definiranje namenskega objekta ali niza zdi pretirano.
- Parametri/tipi vrnitve funkcije: Odlično za definiranje natančnega nabora sprejemljivih nizovnih ali številskih vhodov/izhodov za funkcije.
Primerjava Enumov, Const Assertions in Union Types
Povzemimo ključne razlike in primere uporabe:
Vedenje med Izvajanjem
- Enumi: Ustvarijo JavaScript objekte, potencialno z obratnimi preslikavami.
- Const Assertions (Nizi/Objekti): Ustvarijo navadne JavaScript nize ali objekte. Informacije o tipu se izbrišejo med izvajanjem, vendar podatkovna struktura ostane.
- Union Types (z literali): Ni predstavitve med izvajanjem za sam union. Vrednosti so samo literali. Preverjanje tipov se zgodi izključno med prevajanjem.
Berljivost in Izraznost
- Enumi: Visoka berljivost, zlasti z opisnimi imeni. Lahko so bolj obsežni.
- Const Assertions (Objekti): Dobra berljivost prek parov ključ-vrednost, ki posnemajo konfiguracije ali nastavitve.
- Const Assertions (Nizi): Manj berljivo za predstavljanje poimenovanih konstant, bolj za samo urejen seznam vrednosti.
- Union Types: Zelo jedrnato. Berljivost je odvisna od jasnosti samih literalnih vrednosti.
Varnost Tipov
- Vsi trije pristopi ponujajo močno varnost tipov. Zagotavljajo, da je mogoče spremenljivkam dodeliti ali posredovati funkcijam samo veljavne, vnaprej določene vrednosti.
Velikost Paketa
- Enumi: Na splošno največji zaradi ustvarjenih JavaScript objektov.
- Const Assertions: Manjše od enumov, saj proizvajajo navadne podatkovne strukture.
- Union Types: Najmanjše, saj ne ustvarijo nobene posebne izvajalne podatkovne strukture za sam tip, ampak se zanašajo samo na literalne vrednosti.
Matrika Primerov Uporabe
Tukaj je hiter vodnik:
| Značilnost | TypeScript Enum | Const Assertion (Objekt) | Const Assertion (Niz) | Union Type (Literali) |
|---|---|---|---|---|
| Izhod med Izvajanjem | JS Objekt (z obratno preslikavo) | Navaden JS Objekt | Navaden JS Niz | Brez (samo literalne vrednosti) |
| Berljivost (Poimenovane Konstante) | Visoka | Visoka | Srednja | Nizka (vrednosti so imena) |
| Velikost Paketa | Največja | Srednja | Srednja | Najmanjša |
| Prilagodljivost | Dobra | Dobra | Dobra | Odlična (za preproste nabore) |
| Pogosta Uporaba | Stanja, Kode Stanja, Kategorije | Konfiguracija, Definicije Vloge, Funkcije Zastavic | Urejeni seznami nespremenljivih vrednosti | Parametri funkcije, preproste omejene vrednosti |
Praktični Primeri in Najboljše Prakse
Primer 1: Predstavitev API Kod Stanja
Enum:
enum ApiStatus {
Success = "SUCCESS",
Error = "ERROR",
Pending = "PENDING"
}
function handleApiResponse(status: ApiStatus) {
// ... logic ...
}
Const Assertion (Objekt):
const apiStatusCodes = {
SUCCESS: "SUCCESS",
ERROR: "ERROR",
PENDING: "PENDING"
} as const;
type ApiStatus = typeof apiStatusCodes[keyof typeof apiStatusCodes];
function handleApiResponse(status: ApiStatus) {
// ... logic ...
}
Union Type:
type ApiStatus = "SUCCESS" | "ERROR" | "PENDING";
function handleApiResponse(status: ApiStatus) {
// ... logic ...
}
Priporočilo: Za ta scenarij je union type pogosto najbolj jedrnat in učinkovit. Same literalne vrednosti so dovolj opisne. Če bi morali vsakemu stanju dodeliti dodatne metapodatke (npr. uporabniku prijazno sporočilo), bi bil objekt const assertion boljša izbira.
Primer 2: Definiranje Uporabniških Vlog
Enum:
enum UserRoleEnum {
Admin = "ADMIN",
Moderator = "MODERATOR",
User = "USER"
}
function getUserPermissions(role: UserRoleEnum) {
// ... logic ...
}
Const Assertion (Objekt):
const userRolesObject = {
Admin: "ADMIN",
Moderator: "MODERATOR",
User: "USER"
} as const;
type UserRole = typeof userRolesObject[keyof typeof userRolesObject];
function getUserPermissions(role: UserRole) {
// ... logic ...
}
Union Type:
type UserRole = "ADMIN" | "MODERATOR" | "USER";
function getUserPermissions(role: UserRole) {
// ... logic ...
}
Priporočilo: Objekt const assertion tukaj doseže dobro ravnovesje. Zagotavlja jasne pare ključ-vrednost (npr. userRolesObject.Admin), ki lahko izboljšajo berljivost pri sklicevanju na vloge, hkrati pa je še vedno učinkovit. Union type je prav tako zelo močan tekmec, če so neposredni nizovni literali zadostni.
Primer 3: Predstavitev Možnosti Konfiguracije
Predstavljajte si konfiguracijski objekt za globalno aplikacijo, ki ima lahko različne teme.
Enum:
enum Theme {
Light = "light",
Dark = "dark",
System = "system"
}
interface AppConfig {
theme: Theme;
// ... other config options ...
}
Const Assertion (Objekt):
const themes = {
Light: "light",
Dark: "dark",
System: "system"
} as const;
type Theme = typeof themes[keyof typeof themes];
interface AppConfig {
theme: Theme;
// ... other config options ...
}
Union Type:
type Theme = "light" | "dark" | "system";
interface AppConfig {
theme: Theme;
// ... other config options ...
}
Priporočilo: Za nastavitve konfiguracije, kot so teme, je objekt const assertion pogosto idealen. Jasno definira razpoložljive možnosti in njihove ustrezne nizovne vrednosti. Ključi (Light, Dark, System) so opisni in se neposredno preslikajo v vrednosti, zaradi česar je konfiguracijska koda zelo razumljiva.
Izbira Pravega Orodja za Delo
Odločitev med TypeScript enum, const assertions in union types ni vedno črno-bela. Pogosto gre za kompromis med učinkovitostjo izvajanja, velikostjo paketa in berljivostjo/izraznostjo kode.
- Izberite Union Types, ko potrebujete preprost, omejen nabor nizovnih ali številskih literalov in je zaželena največja jedrnatost. Odlični so za podpise funkcij in osnovne omejitve vrednosti.
- Izberite Const Assertions (z Objekti), ko želite bolj strukturiran, berljiv način za definiranje poimenovanih konstant, podobno kot enum, vendar z bistveno manj izvajalne obremenitve. To je odlično za konfiguracijo, vloge ali kateri koli nabor, kjer ključi dodajo pomemben pomen.
- Izberite Const Assertions (z Nizami), ko preprosto potrebujete nespremenljiv urejen seznam vrednosti in je neposreden dostop prek indeksa pomembnejši od poimenovanih ključev.
- Razmislite o TypeScript Enumih, ko potrebujete njihove posebne funkcije, kot je obratna preslikava (čeprav je to v sodobnem razvoju manj pogosto), ali če ima vaša ekipa močno preferenco in je vpliv na učinkovitost zanemarljiv za vaš projekt.
V številnih sodobnih TypeScript projektih boste našli nagnjenost k const assertions in union types pred tradicionalnimi enummi, zlasti za konstante, ki temeljijo na nizih, zaradi njihovih boljših zmogljivosti in pogosto preprostejšega izhoda JavaScript.
Globalni Premisleki
Pri razvoju aplikacij za globalno občinstvo so dosledne in predvidljive definicije konstant ključnega pomena. Izbire, o katerih smo razpravljali (enumi, const assertions, union types), vse prispevajo k tej doslednosti z uveljavljanjem varnosti tipov v različnih okoljih in med razvijalci.
- Doslednost: Ne glede na izbrano metodo je ključna doslednost znotraj vašega projekta. Če se odločite uporabiti objekte const assertion za vloge, se držite tega vzorca v celotni bazi kode.
- Internacionalizacija (i18n): Pri definiranju etiket ali sporočil, ki bodo internacionalizirana, uporabite te varnostne strukture tipov, da zagotovite, da se uporabljajo samo veljavni ključi ali identifikatorji. Dejanske prevedene nize bodo upravljali ločeno prek knjižnic i18n. Na primer, če imate polje `status`, ki je lahko "PENDING", "PROCESSING", "COMPLETED", bo vaša knjižnica i18n preslikala te notranje identifikatorje v lokalizirano prikazano besedilo.
- Časovni Pasovi & Valute: Čeprav ni neposredno povezano z enummi, ne pozabite, da vam lahko pri obravnavanju vrednosti, kot so datumi, časi ali valute, sistem tipov TypeScript pomaga uveljaviti pravilno uporabo, vendar so za natančno globalno obravnavo običajno potrebne zunanje knjižnice. Na primer, `Currency` union type bi lahko bil definiran kot `"USD" | "EUR" | "GBP"`, vendar dejanska logika pretvorbe zahteva specializirana orodja.
Zaključek
TypeScript ponuja bogat nabor orodij za upravljanje konstant. Medtem ko so nam enumi dobro služili, const assertions in union types ponujajo prepričljive, pogosto učinkovitejše alternative. Z razumevanjem njihovih razlik in izbiro pravega pristopa glede na vaše specifične potrebe - pa naj bo to učinkovitost, berljivost ali jedrnatost - lahko pišete bolj robustno, vzdržljivo in učinkovito kodo TypeScript, ki se globalno razširja.
Sprejemanje teh alternativ lahko vodi do manjših velikosti paketov, hitrejših aplikacij in bolj predvidljive izkušnje za vašo mednarodno ekipo.