Izpētiet TypeScript enum alternatīvas (konstantu apgalvojumus, savienojuma tipus). Izprotiet ieguvumus, trūkumus un pielietojumu tīram, uzturamam kodam globāli.
TypeScript Enum alternatīvas: Konstantu apgalvojumu un savienojuma tipu izmantošana stabilam kodam
TypeScript, jaudīga JavaScript supersets, ienes statisko tipizāciju dinamiskajā tīmekļa izstrādes pasaulē. Starp daudzajām funkcijām, enum atslēgvārds jau sen ir bijis populārs veids, kā definēt nosauktu konstantu kopumu. Enumi nodrošina skaidru veidu, kā attēlot fiksētu saistītu vērtību kolekciju, uzlabojot lasāmību un tipu drošību.
Tomēr, TypeScript ekosistēmai nobriestot un projektiem pieaugot sarežģītībā un mērogā, izstrādātāji visā pasaulē arvien vairāk apšauba tradicionālo enumu lietderību. Lai gan tie ir vienkārši vienkāršos gadījumos, enumi ievieš noteiktas uzvedības un izpildlaika īpašības, kas dažkārt var radīt negaidītas problēmas, ietekmēt pakotnes izmēru vai sarežģīt tree-shaking optimizācijas. Tas ir novedis pie plašas alternatīvu izpētes.
Šis visaptverošais ceļvedis padziļināti pēta divas ievērojamas un ļoti efektīvas alternatīvas TypeScript enum tipiem: Savienojuma tipi ar virkņu/skaitliskajiem literāļiem un Konstantu apgalvojumi (as const). Mēs izpētīsim to mehānismus, praktisko pielietojumu, priekšrocības un kompromisus, sniedzot jums zināšanas, lai pieņemtu pārdomātus dizaina lēmumus saviem projektiem, neatkarīgi no to lieluma vai globālās komandas, kas pie tiem strādā. Mūsu mērķis ir dot jums iespēju rakstīt stabilāku, uzturamāku un efektīvāku TypeScript kodu.
TypeScript Enum: Īss kopsavilkums
Pirms iedziļināmies alternatīvās, īsi apskatīsim tradicionālo TypeScript enum. Enumi ļauj izstrādātājiem definēt nosauktu konstantu kopumu, padarot kodu lasāmāku un novēršot "maģisko virkņu" vai "maģisko skaitļu" izkliedēšanu visā lietojumprogrammā. Tie ir divos galvenajos veidos: skaitliskie un virkņu enumi.
Skaitliskie Enumi
Pēc noklusējuma TypeScript enumi ir skaitliski. Pirmais elements tiek inicializēts ar 0, un katrs nākamais elements tiek automātiski palielināts.
enum Direction {
Up,
Down,
Left,
Right,
}
let currentDirection: Direction = Direction.Up;
console.log(currentDirection); // Outputs: 0
console.log(Direction.Left); // Outputs: 2
Varat arī manuāli inicializēt skaitlisko enum elementus:
enum StatusCode {
Success = 200,
NotFound = 404,
ServerError = 500,
}
let status: StatusCode = StatusCode.NotFound;
console.log(status); // Outputs: 404
Īpaša skaitlisko enumu iezīme ir reversā kartēšana. Izpildlaikā skaitliskais enums tiek kompilēts JavaScript objektā, kas kartē gan nosaukumus uz vērtībām, gan vērtības atpakaļ uz nosaukumiem.
enum UserRole {
Admin = 1,
Editor,
Viewer,
}
console.log(UserRole[1]); // Outputs: "Admin"
console.log(UserRole.Editor); // Outputs: 2
console.log(UserRole[2]); // Outputs: "Editor"
/*
Compiles to JavaScript:
var UserRole;
(function (UserRole) {
UserRole[UserRole["Admin"] = 1] = "Admin";
UserRole[UserRole["Editor"] = 2] = "Editor";
UserRole[UserRole["Viewer"] = 3] = "Viewer";
})(UserRole || (UserRole = {}));
*/
Virkņu Enumi
Virkņu enumi bieži tiek doti priekšroka to lasāmības dēļ izpildlaikā, jo tie nepaļaujas uz automātiski palielināmiem skaitļiem. Katrs elements ir jāinicializē ar virknes literāli.
enum UserPermission {
Read = "READ_PERMISSION",
Write = "WRITE_PERMISSION",
Delete = "DELETE_PERMISSION",
}
let permission: UserPermission = UserPermission.Write;
console.log(permission); // Outputs: "WRITE_PERMISSION"
Virkņu enumi nesaņem reverso kartēšanu, kas parasti ir labi, lai izvairītos no negaidītas izpildlaika uzvedības un samazinātu ģenerēto JavaScript izvadi.
Galvenie apsvērumi un iespējamās enumu kļūmes
Lai gan enumi piedāvā ērtības, tiem ir noteiktas īpašības, kas prasa rūpīgu apsvēršanu:
- Izpildlaika objekti: Gan skaitliskie, gan virkņu enumi izpildlaikā ģenerē JavaScript objektus. Tas nozīmē, ka tie veicina jūsu lietojumprogrammas pakotnes izmēru, pat ja tos izmantojat tikai tipu pārbaudei. Maziem projektiem tas var būt nenozīmīgs, taču liela mēroga lietojumprogrammās ar daudziem enumiem tas var summēties.
- Tree-Shaking trūkums: Tā kā enumi ir izpildlaika objekti, modernie bundleri, piemēram, Webpack vai Rollup, tos bieži vien neefektīvi tree-shake. Ja definējat enum, bet izmantojat tikai vienu vai divus tā elementus, viss enum objekts joprojām var tikt iekļauts jūsu gala pakotnē. Tas var novest pie lielākiem failu izmēriem, nekā nepieciešams.
- Reversā kartēšana (skaitliskie enumi): Skaitlisko enumu reversās kartēšanas funkcija, lai gan dažkārt noderīga, var būt arī apjukuma un negaidītas uzvedības avots. Tā pievieno papildu kodu JavaScript izvadei un ne vienmēr var būt vēlamā funkcionalitāte. Piemēram, skaitlisko enumu serializēšana dažkārt var novest pie tā, ka tiek saglabāts tikai skaitlis, kas var nebūt tik aprakstošs kā virkne.
- Transpilācijas pieskaitāmās izmaksas: Enumu kompilēšana JavaScript objektos rada nelielas papildu izmaksas būvēšanas procesā salīdzinājumā ar vienkāršu konstantu mainīgo definēšanu.
- Ierobežota iterācija: Tieša iterācija pa enum vērtībām var būt sarežģīta, īpaši ar skaitliskajiem enum dēļ reversās kartēšanas. Lai iegūtu tikai vēlamās vērtības, bieži ir nepieciešamas palīgfunkcijas vai specifiskas cilpas.
Šie punkti uzsver, kāpēc daudzas globālās izstrādes komandas, īpaši tās, kas koncentrējas uz veiktspēju un pakotnes izmēru, meklē alternatīvas, kas nodrošina līdzīgu tipu drošību bez izpildlaika nospieduma vai citām sarežģītībām.
Alternatīva 1: Savienojuma tipi ar literāļiem
Viena no vienkāršākajām un jaudīgākajām alternatīvām enumiem TypeScript ir Savienojuma tipu izmantošana ar virkņu vai skaitliskajiem literāļiem. Šī pieeja izmanto TypeScript robusto tipu sistēmu, lai kompilācijas laikā definētu specifisku, atļautu vērtību kopumu, neieviešot nekādas jaunas konstrukcijas izpildlaikā.
Kas ir savienojuma tipi?
Savienojuma tips apraksta vērtību, kas var būt viens no vairākiem tipiem. Piemēram, string | number nozīmē, ka mainīgais var glabāt vai nu virkni, vai skaitli. Apvienojot ar literāļu tipiem (piemēram, "success", 404), varat definēt tipu, kas var glabāt tikai noteiktu iepriekš definētu vērtību kopumu.
Praktisks piemērs: Statusu definēšana ar savienojuma tipiem
Apskatīsim bieži sastopamu scenāriju: iespējamo statusu kopuma definēšana datu apstrādes darbam vai lietotāja kontam. Ar savienojuma tipiem tas izskatās tīri un kodolīgi:
type JobStatus = "PENDING" | "IN_PROGRESS" | "COMPLETED" | "FAILED";
function processJob(status: JobStatus): void {
if (status === "COMPLETED") {
console.log("Job finished successfully.");
} else if (status === "FAILED") {
console.log("Job encountered an error.");
} else {
console.log(`Job is currently ${status}.`);
}
}
let currentJobStatus: JobStatus = "IN_PROGRESS";
processJob(currentJobStatus);
// This would result in a compile-time error:
// let invalidStatus: JobStatus = "CANCELLED"; // Error: Type '"CANCELLED"' is not assignable to type 'JobStatus'.
Skaitliskajām vērtībām raksts ir identisks:
type HttpCode = 200 | 400 | 404 | 500;
function handleResponse(code: HttpCode): void {
if (code === 200) {
console.log("Operation successful.");
} else if (code === 404) {
console.log("Resource not found.");
}
}
let responseStatus: HttpCode = 200;
handleResponse(responseStatus);
Ievērojiet, kā mēs šeit definējam type aizstājvārdu. Tā ir tīri kompilācijas laika konstrukcija. Kad tas tiek kompilēts JavaScript, JobStatus vienkārši pazūd, un literālie virknes/skaitļi tiek izmantoti tieši.
Savienojuma tipu priekšrocības ar literāļiem
Šī pieeja piedāvā vairākas pārliecinošas priekšrocības:
- Tīri kompilācijas laikā: Savienojuma tipi kompilācijas laikā tiek pilnībā dzēsti. Tie neģenerē nekādu JavaScript kodu izpildlaikā, tādējādi samazinot pakotnes izmēru un paātrinot lietojumprogrammas starta laiku. Tā ir būtiska priekšrocība veiktspējīgi kritiskām lietojumprogrammām un tām, kas izvietotas globāli, kur katrs kilobaits ir svarīgs.
- Izcila tipu drošība: TypeScript rūpīgi pārbauda piešķiršanu pret definētajiem literāļu tipiem, nodrošinot spēcīgas garantijas, ka tiek izmantotas tikai derīgas vērtības. Tas novērš bieži sastopamas kļūdas, kas saistītas ar pareizrakstības kļūdām vai nepareizām vērtībām.
- Optimāla Tree-Shaking: Tā kā nav izpildlaika objekta, savienojuma tipi pēc būtības atbalsta tree-shaking. Jūsu bundleris iekļauj tikai faktiskos virkņu vai skaitlisko literāļus, kurus izmantojat, nevis visu objektu.
- Lasāmība: Fiksētam vienkāršu, atšķirīgu vērtību kopumam tipa definīcija bieži ir ļoti skaidra un viegli saprotama.
- Vienkāršība: Nav ieviestas jaunas valodu konstrukcijas vai sarežģīti kompilācijas artefakti. Tiek izmantotas tikai fundamentālas TypeScript tipu funkcijas.
- Tieša vērtību piekļuve: Jūs tieši strādājat ar virknes vai skaitļa vērtībām, kas vienkāršo serializāciju un deserializāciju, īpaši, mijiedarbojoties ar API vai datu bāzēm, kas sagaida specifiskus virknes identifikatorus.
Savienojuma tipu trūkumi ar literāļiem
Lai gan savienojuma tipi ir jaudīgi, tiem ir arī daži ierobežojumi:
- Atkārtošana saistītajiem datiem: Ja jums ir jāpievieno papildu dati vai metadati katram "enum" elementam (piemēram, attēlojamais nosaukums, ikona, krāsa), to nevar izdarīt tieši savienojuma tipa definīcijā. Parasti būtu nepieciešams atsevišķs kartēšanas objekts.
- Nav tiešas visu vērtību iterācijas: Nav iebūvēta veida, kā izpildlaikā iegūt visu iespējamo vērtību masīvu no savienojuma tipa. Piemēram, jūs nevarat viegli iegūt
["PENDING", "IN_PROGRESS", "COMPLETED", "FAILED"]tieši noJobStatus. Tas bieži vien prasa atsevišķa vērtību masīva uzturēšanu, ja tās ir jāattēlo lietotāja saskarnē (piemēram, nolaižamajā izvēlnē). - Mazāk centralizēts: Ja vērtību kopums ir nepieciešams gan kā tips, gan kā izpildlaika vērtību masīvs, jūs varat nonākt pie tā, ka saraksts tiek definēts divreiz (vienreiz kā tips, vienreiz kā izpildlaika masīvs), kas var radīt desinhronizācijas potenciālu.
Neskatoties uz šiem trūkumiem, daudzos scenārijos savienojuma tipi nodrošina tīru, veiktspējīgu un tipu drošu risinājumu, kas labi saskan ar modernām JavaScript izstrādes praksēm.
Alternatīva 2: Konstantu apgalvojumi (as const)
as const apgalvojums, kas tika ieviests TypeScript 3.4, ir vēl viens neticami jaudīgs rīks, kas piedāvā izcilu alternatīvu enumiem, īpaši, ja jums ir nepieciešams izpildlaika objekts un robusta tipu secināšana. Tas ļauj TypeScript secināt šaurāko iespējamo tipu literālajām izteiksmēm.
Kas ir konstantu apgalvojumi?
Kad jūs lietojat as const mainīgajam, masīvam vai objekta literālim, TypeScript apstrādā visas šī literāļa īpašības kā readonly un secina to literāļu tipus, nevis plašākus tipus (piemēram, "foo", nevis string, 123, nevis number). Tas ļauj iegūt ļoti specifiskus savienojuma tipus no izpildlaika datu struktūrām.
Praktisks piemērs: "Pseido-enum" objekta izveide ar as const
Atgriezīsimies pie mūsu darba statusa piemēra. Izmantojot as const, mēs varam definēt vienotu patiesības avotu mūsu statusiem, kas darbojas gan kā izpildlaika objekts, gan kā pamats tipu definīcijām.
const JobStatuses = {
PENDING: "PENDING",
IN_PROGRESS: "IN_PROGRESS",
COMPLETED: "COMPLETED",
FAILED: "FAILED",
} as const;
// JobStatuses.PENDING is now inferred as type "PENDING" (not just string)
// JobStatuses is inferred as type {
// readonly PENDING: "PENDING";
// readonly IN_PROGRESS: "IN_PROGRESS";
// readonly COMPLETED: "COMPLETED";
// readonly FAILED: "FAILED";
// }
Šajā brīdī JobStatuses ir JavaScript objekts izpildlaikā, tieši tāpat kā parasts enums. Tomēr tā tipu secināšana ir daudz precīzāka.
Apvienošana ar typeof un keyof savienojuma tipiem
Patiesais spēks parādās, apvienojot as const ar TypeScript operatoriem typeof un keyof, lai iegūtu savienojuma tipu no objekta vērtībām vai atslēgām.
const JobStatuses = {
PENDING: "PENDING",
IN_PROGRESS: "IN_PROGRESS",
COMPLETED: "COMPLETED",
FAILED: "FAILED",
} as const;
// Type representing the keys (e.g., "PENDING" | "IN_PROGRESS" | ...)
type JobStatusKeys = keyof typeof JobStatuses;
// Type representing the values (e.g., "PENDING" | "IN_PROGRESS" | ...)
type JobStatusValues = typeof JobStatuses[keyof typeof JobStatuses];
function processJobWithConstAssertion(status: JobStatusValues): void {
if (status === JobStatuses.COMPLETED) {
console.log("Job finished successfully.");
} else if (status === JobStatuses.FAILED) {
console.log("Job encountered an error.");
} else {
console.log(`Job is currently ${status}.`);
}
}
let currentJobStatusFromObject: JobStatusValues = JobStatuses.IN_PROGRESS;
processJobWithConstAssertion(currentJobStatusFromObject);
// This would result in a compile-time error:
// let invalidStatusFromObject: JobStatusValues = "CANCELLED"; // Error!
Šis raksts nodrošina labāko no abām pasaulēm: izpildlaika objektu iterācijai vai tiešai īpašību piekļuvei, un kompilācijas laika savienojuma tipu stingrai tipu pārbaudei.
Konstantu apgalvojumu priekšrocības ar atvasinātajiem savienojuma tipiem
- Vienots patiesības avots: Jūs definējat savas konstantes vienreiz vienkāršā JavaScript objektā un no tā atvasināt gan izpildlaika piekļuvi, gan kompilācijas laika tipus. Tas ievērojami samazina dublēšanos un uzlabo uzturēšanu dažādās izstrādes komandās.
- Tipu drošība: Līdzīgi kā ar tīriem savienojuma tipiem, jūs iegūstat izcilu tipu drošību, nodrošinot, ka tiek izmantotas tikai iepriekš definētas vērtības.
- Iterējamība izpildlaikā: Tā kā
JobStatusesir vienkāršs JavaScript objekts, jūs varat viegli iterēt pa tā atslēgām vai vērtībām, izmantojot standarta JavaScript metodes, piemēram,Object.keys(),Object.values()vaiObject.entries(). Tas ir nenovērtējami dinamiskām lietotāja saskarnēm (piemēram, nolaižamo izvēlņu aizpildīšanai) vai žurnālēšanai. - Saistītie dati: Šis raksts dabiski atbalsta papildu datu saistīšanu ar katru "enum" elementu.
- Labāks Tree-Shaking potenciāls (salīdzinājumā ar enum tipiem): Lai gan
as constrada izpildlaika objektu, tas ir standarta JavaScript objekts. Mūsdienu bundleri parasti efektīvāk veic neizmantoto šī objekta īpašību vai pat veselu objektu tree-shaking, ja uz tām nav atsauču, salīdzinājumā ar TypeScript ģenerēto enum kompilācijas izvadi. Tomēr, ja objekts ir liels un tiek izmantotas tikai dažas īpašības, viss objekts joprojām var tikt iekļauts, ja tas tiek importēts tādā veidā, kas novērš granulētu tree-shaking. - Elastīgums: Varat definēt vērtības, kas nav tikai virknes vai skaitļi, bet arī sarežģītāki objekti, ja nepieciešams, padarot šo rakstu ļoti elastīgu.
const FileOperations = {
UPLOAD: {
label: "Upload File",
icon: "upload-icon.svg",
permission: "can_upload"
},
DOWNLOAD: {
label: "Download File",
icon: "download-icon.svg",
permission: "can_download"
},
DELETE: {
label: "Delete File",
icon: "delete-icon.svg",
permission: "can_delete"
},
} as const;
type FileOperationType = keyof typeof FileOperations; // "UPLOAD" | "DOWNLOAD" | "DELETE"
type FileOperationDetail = typeof FileOperations[keyof typeof FileOperations]; // { label: string; icon: string; permission: string; }
function performOperation(opType: FileOperationType) {
const details = FileOperations[opType];
console.log(`Performing: ${details.label} (Permission: ${details.permission})`);
}
performOperation("UPLOAD");
Konstantu apgalvojumu trūkumi
- Izpildlaika objekta klātbūtne: Atšķirībā no tīriem savienojuma tipiem, šī pieeja joprojām rada JavaScript objektu izpildlaikā. Lai gan tas ir standarta objekts un bieži vien labāk piemērots tree-shaking nekā enumi, tas netiek pilnībā dzēsts.
- Nedaudz garāka tipa definīcija: Savienojuma tipa atvasināšanai (
keyof typeof ...vaitypeof ...[keyof typeof ...]) ir nepieciešama nedaudz vairāk sintakses, nekā vienkārši uzskaitot literāļus savienojuma tipam. - Iespējama nepareiza lietošana: Ja netiek lietots uzmanīgi, ļoti liels
as constobjekts joprojām var būtiski palielināt pakotnes izmēru, ja tā saturs netiek efektīvi tree-shake pāri moduļu robežām.
Scenārijiem, kur nepieciešama gan robusta kompilācijas laika tipu pārbaude, gan izpildlaika vērtību kolekcija, ko var iterēt vai kas nodrošina saistītos datus, as const bieži ir vēlamā izvēle starp TypeScript izstrādātājiem visā pasaulē.
Alternatīvu salīdzināšana: Kad ko izmantot?
Izvēle starp savienojuma tipiem un konstantu apgalvojumiem lielā mērā ir atkarīga no jūsu specifiskajām prasībām attiecībā uz izpildlaika klātbūtni, iterējamību un to, vai jums ir nepieciešams saistīt papildu datus ar savām konstantēm. Izanalizēsim lēmumu pieņemšanas faktorus.
Vienkāršība pret stabilitāti
- Savienojuma tipi: Piedāvā galīgu vienkāršību, ja jums kompilācijas laikā ir nepieciešams tikai tipu drošs atšķirīgu virkņu vai skaitlisko vērtību kopums. Tie ir visvieglākais variants.
- Konstantu apgalvojumi: Nodrošina stabilāku rakstu, ja jums ir nepieciešama gan kompilācijas laika tipu drošība, gan izpildlaika objekts, ko var vaicāt, iterēt vai paplašināt ar papildu metadatiem. Sākotnējā iestatīšana ir nedaudz apjomīgāka, taču tā atmaksājas ar funkcijām.
Izpildlaiks pret kompilācijas laiku
- Savienojuma tipi: Ir tīri kompilācijas laika konstrukcijas. Tie absolūti neģenerē nekādu JavaScript kodu. Tas ir ideāli piemērots lietojumprogrammām, kurās ir vissvarīgāk minimizēt pakotnes izmēru, un pašas vērtības ir pietiekamas, nepieprasot piekļuvi tām kā objektam izpildlaikā.
- Konstantu apgalvojumi: Ģenerē vienkāršu JavaScript objektu izpildlaikā. Šis objekts ir pieejams un izmantojams jūsu JavaScript kodā. Lai gan tas palielina pakotnes izmēru, tas parasti ir efektīvāks nekā TypeScript enumi un labāks kandidāts tree-shaking.
Iterējamības prasības
- Savienojuma tipi: Nepiedāvā tiešu veidu, kā izpildlaikā iterēt pa visām iespējamām vērtībām. Ja jums ir jāaizpilda nolaižamā izvēlne vai jāattēlo visas opcijas, jums būs jādefinē atsevišķs šo vērtību masīvs, kas potenciāli var radīt dublēšanos.
- Konstantu apgalvojumi: Šeit tie ir izcili. Tā kā strādājat ar standarta JavaScript objektu, varat viegli izmantot
Object.keys(),Object.values()vaiObject.entries(), lai iegūtu atslēgu, vērtību vai atslēgu-vērtību pāru masīvu. Tas padara tos ideāli piemērotus dinamiskām lietotāja saskarnēm (piemēram, nolaižamo izvēlņu aizpildīšanai) vai žurnālēšanai.
const PaymentMethods = {
CREDIT_CARD: "Credit Card",
PAYPAL: "PayPal",
BANK_TRANSFER: "Bank Transfer",
} as const;
type PaymentMethodType = keyof typeof PaymentMethods;
// Get all keys (e.g., for internal logic)
const methodKeys = Object.keys(PaymentMethods) as PaymentMethodType[];
console.log(methodKeys); // ["CREDIT_CARD", "PAYPAL", "BANK_TRANSFER"]
// Get all values (e.g., for display in a dropdown)
const methodLabels = Object.values(PaymentMethods);
console.log(methodLabels); // ["Credit Card", "PayPal", "Bank Transfer"]
// Get key-value pairs (e.g., for mapping)
const methodEntries = Object.entries(PaymentMethods);
console.log(methodEntries); // [["CREDIT_CARD", "Credit Card"], ...]
Tree-Shaking sekas
- Savienojuma tipi: Pēc būtības ir tree-shakeable, jo tie pastāv tikai kompilācijas laikā.
- Konstantu apgalvojumi: Lai gan tie rada izpildlaika objektu, modernie bundleri bieži vien var efektīvāk veikt neizmantoto šī objekta īpašību tree-shaking nekā ar TypeScript ģenerētajiem enum objektiem. Tomēr, ja viss objekts tiek importēts un uz to ir atsauce, tas, visticamāk, tiks iekļauts. Rūpīgs moduļu dizains var palīdzēt.
Labākā prakse un hibrīdās pieejas
Tas ne vienmēr ir "vai nu/vai" situācija. Bieži vien labākais risinājums ietver hibrīdu pieeju, īpaši lielās, internacionalizētās lietojumprogrammās:
- Vienkāršiem, tīri iekšējiem karodziņiem vai identifikatoriem, kurus nekad nav nepieciešams iterēt vai kuriem nav saistītu datu, Savienojuma tipi parasti ir visveiktspējīgākā un tīrākā izvēle.
- Konstantu kopumiem, kurus ir nepieciešams iterēt, attēlot lietotāja saskarnēs vai kuriem ir bagātīgi saistīti metadati (piemēram, nosaukumi, ikonas vai atļaujas), Konstantu apgalvojumu raksts ir pārāks.
- Apvienošana lasāmībai un lokalizācijai: Daudzas komandas izmanto
as constiekšējiem identifikatoriem un pēc tam iegūst lokalizētus attēlošanas nosaukumus no atsevišķas internacionalizācijas (i18n) sistēmas.
// src/constants/order-status.ts
const OrderStatuses = {
PENDING: "PENDING",
PROCESSING: "PROCESSING",
SHIPPED: "SHIPPED",
DELIVERED: "DELIVERED",
CANCELLED: "CANCELLED",
} as const;
type OrderStatus = typeof OrderStatuses[keyof typeof OrderStatuses];
export { OrderStatuses, type OrderStatus };
// src/i18n/en.json
{
"orderStatus": {
"PENDING": "Pending Confirmation",
"PROCESSING": "Processing Order",
"SHIPPED": "Shipped",
"DELIVERED": "Delivered",
"CANCELLED": "Cancelled"
}
}
// src/i18n/locales/es.json
{
"productCategories": {
"ELECTRONICS": "Electrónica",
"APPAREL": "Ropa y Accesorios",
"HOME_GOODS": "Artículos para el hogar",
"BOOKS": "Libros"
}
}
// src/components/ProductCategorySelector.tsx
import { ProductCategories, type ProductCategory } from "../features/product/constants";
import { useTranslation } from "react-i18next";
function ProductCategorySelector() {
const { t } = useTranslation();
return (
<select>
{Object.values(ProductCategories).map(categoryKey => (
<option key={categoryKey} value={categoryKey}>
{t(`productCategories.${categoryKey}`)}
</option>
))}
</select>
);
}
Šī hibrīdā pieeja izmanto as const tipu drošību un izpildlaika iterējamību, vienlaikus saglabājot lokalizētās attēlošanas virknes atsevišķi un viegli pārvaldāmas, kas ir kritisks apsvērums globālām lietojumprogrammām.
Uzlaboti raksti un apsvērumi
Papildus pamata lietošanai gan savienojuma tipus, gan konstantu apgalvojumus var integrēt sarežģītākos rakstos, lai vēl vairāk uzlabotu koda kvalitāti un uzturēšanu.
Tipu aizsargu izmantošana ar savienojuma tipiem
Strādājot ar savienojuma tipiem, īpaši, ja savienojums ietver dažādus tipus (ne tikai literāļus), tipu aizsargi kļūst būtiski tipu sašaurināšanai. Ar literālu savienojuma tipiem diskriminētie savienojumi piedāvā milzīgu spēku.
type SuccessEvent = { type: "SUCCESS"; data: any; };
type ErrorEvent = { type: "ERROR"; message: string; code: number; };
type SystemEvent = SuccessEvent | ErrorEvent;
function handleSystemEvent(event: SystemEvent) {
if (event.type === "SUCCESS") {
console.log("Data received:", event.data);
// event is now narrowed to SuccessEvent
} else {
console.log("Error occurred:", event.message, "Code:", event.code);
// event is now narrowed to ErrorEvent
}
}
handleSystemEvent({ type: "SUCCESS", data: { user: "Alice" } });
handleSystemEvent({ type: "ERROR", message: "Network failure", code: 503 });
Šis raksts, ko bieži dēvē par "diskriminētajiem savienojumiem", ir neticami stabils un tipu drošs, nodrošinot kompilācijas laika garantijas par jūsu datu struktūru, pamatojoties uz kopīgu literālo īpašību (diskriminatoru).
Object.values() ar as const un tipu apgalvojumiem
Izmantojot as const rakstu, Object.values() var būt ļoti noderīgs. Tomēr TypeScript noklusējuma secināšana Object.values() var būt plašāka, nekā vēlamais (piemēram, string[], nevis specifisks literāļu savienojums). Stingrībai jums var būt nepieciešams tipa apgalvojums.
const Statuses = {
ACTIVE: "Active",
INACTIVE: "Inactive",
PENDING: "Pending",
} as const;
type StatusValue = typeof Statuses[keyof typeof Statuses]; // "Active" | "Inactive" | "Pending"
// Object.values(Statuses) is inferred as (string | "Active" | "Inactive" | "Pending")[]
// We can assert it more narrowly if needed:
const allStatusValues: StatusValue[] = Object.values(Statuses);
console.log(allStatusValues); // ["Active", "Inactive", "Pending"]
// For a dropdown, you might pair values with labels if they differ
const statusOptions = Object.entries(Statuses).map(([key, value]) => ({
value: key, // Use the key as the actual identifier
label: value // Use the value as the display label
}));
console.log(statusOptions);
/*
[
{ value: "ACTIVE", label: "Active" },
{ value: "INACTIVE", label: "Inactive" },
{ value: "PENDING", label: "Pending" }
]
*/
Tas parāda, kā iegūt stingri tipizētu vērtību masīvu, kas piemērots lietotāja saskarnes elementiem, vienlaikus saglabājot literālu tipus.
Internacionalizācija (i18n) un lokalizētie nosaukumi
Globālām lietojumprogrammām lokalizētu virkņu pārvaldība ir vissvarīgākā. Lai gan TypeScript enumi un to alternatīvas nodrošina iekšējos identifikatorus, attēlošanas nosaukumi bieži ir jāatdala i18n vajadzībām. as const raksts lieliski papildina i18n sistēmas.
Jūs definējat savus iekšējos, nemainīgos identifikatorus, izmantojot as const. Šie identifikatori ir konsekventi visos lokalizācijas reģionos un kalpo kā atslēgas jūsu tulkošanas failiem. Faktiskās attēlošanas virknes tiek iegūtas no i18n bibliotēkas (piemēram, react-i18next, vue-i18n, FormatJS), pamatojoties uz lietotāja izvēlēto valodu.
// app/features/product/constants.ts
export const ProductCategories = {
ELECTRONICS: "ELECTRONICS",
APPAREL: "APPAREL",
HOME_GOODS: "HOME_GOODS",
BOOKS: "BOOKS",
} as const;
export type ProductCategory = typeof ProductCategories[keyof typeof ProductCategories];
// app/i18n/locales/en.json
{
"productCategories": {
"ELECTRONICS": "Electronics",
"APPAREL": "Apparel & Accessories",
"HOME_GOODS": "Home Goods",
"BOOKS": "Books"
}
}
// app/i18n/locales/es.json
{
"productCategories": {
"ELECTRONICS": "Electrónica",
"APPAREL": "Ropa y Accesorios",
"HOME_GOODS": "Artículos para el hogar",
"BOOKS": "Libros"
}
}
// app/components/ProductCategorySelector.tsx
import { ProductCategories, type ProductCategory } from "../features/product/constants";
import { useTranslation }s(ProductCategories).map(categoryKey => (
<option key={categoryKey} value={categoryKey}>
{t(`productCategories.${categoryKey}`)}
</option>
))}
</select>
);
}
Šī interešu nodalīšana ir ļoti svarīga mērogojamām, globālām lietojumprogrammām. TypeScript tipi nodrošina, ka jūs vienmēr izmantojat derīgas atslēgas, un i18n sistēma apstrādā prezentācijas slāni, pamatojoties uz lietotāja lokalizāciju. Tas novērš valodu atkarīgu virkņu tiešu iegulšanu jūsu pamatlietojumprogrammas loģikā, kas ir bieži sastopama anti-pattern starptautiskām komandām.
Secinājums: Jūsu TypeScript dizaina izvēļu pilnvarošana
TypeScript turpinot attīstīties un dot iespēju izstrādātājiem visā pasaulē veidot stabilākas un mērogojamākas lietojumprogrammas, tā niansēto funkciju un alternatīvu izpratne kļūst arvien svarīgāka. Lai gan TypeScript enum atslēgvārds piedāvā ērtu veidu, kā definēt nosauktas konstantes, tā izpildlaika nospiedums, tree-shaking ierobežojumi un reversās kartēšanas sarežģītība bieži padara modernas alternatīvas pievilcīgākas veiktspējas jutīgiem vai liela mēroga projektiem.
Savienojuma tipi ar virkņu/skaitliskajiem literāļiem izceļas kā visvieglākais un visvairāk uz kompilācijas laiku orientētais risinājums. Tie nodrošina bezkompromisu tipu drošību, neģenerējot nekādu JavaScript izpildlaikā, padarot tos ideāli piemērotus scenārijiem, kur minimāls pakotnes izmērs un maksimāls tree-shaking ir prioritātes, un izpildlaika uzskaitījums nav problēma.
No otras puses, Konstantu apgalvojumi (as const) apvienojumā ar typeof un keyof piedāvā ļoti elastīgu un jaudīgu rakstu. Tie nodrošina vienotu patiesības avotu jūsu konstantēm, spēcīgu kompilācijas laika tipu drošību un kritiski svarīgo spēju iterēt pa vērtībām izpildlaikā. Šī pieeja ir īpaši piemērota situācijām, kurās jums ir jāsaista papildu dati ar jūsu konstantēm, jāaizpilda dinamiskas lietotāja saskarnes vai jāintegrējas ar internacionalizācijas sistēmām.
Rūpīgi apsverot kompromisus – izpildlaika nospiedumu, iterējamības vajadzības un saistīto datu sarežģītību –, varat pieņemt pārdomātus lēmumus, kas noved pie tīrāka, efektīvāka un uzturamāka TypeScript koda. Šo alternatīvu pieņemšana nav tikai par "moderna" TypeScript rakstīšanu; tā ir par apzinātu arhitektonisku izvēli, kas uzlabo jūsu lietojumprogrammas veiktspēju, izstrādātāju pieredzi un ilgtermiņa ilgtspēju globālai auditorijai.
Pilnvarojiet savu TypeScript izstrādi, izvēloties pareizo rīku pareizajam uzdevumam, pārsniedzot noklusējuma enum, ja pastāv labākas alternatīvas.