Utforska TypeScripts kraftfulla template literal-typer för avancerad strÀngmanipulation, mönstermatchning och validering. LÀr dig med praktiska exempel.
Template Literal-typer: StrÀngmönstermatchning och validering i TypeScript
TypeScripts typsystem utvecklas stÀndigt och erbjuder utvecklare allt kraftfullare verktyg för att uttrycka komplex logik och sÀkerstÀlla typsÀkerhet. En av de mest intressanta och mÄngsidiga funktionerna som introducerats i de senaste versionerna Àr template literal-typer. Dessa typer lÄter dig manipulera strÀngar pÄ typnivÄ, vilket möjliggör avancerad strÀngmönstermatchning och validering. Detta öppnar upp en helt ny vÀrld av möjligheter för att skapa mer robusta och underhÄllbara applikationer.
Vad Àr Template Literal-typer?
Template literal-typer Àr en form av typ som konstrueras genom att kombinera strÀngliteral-typer och union-typer, liknande hur template literals fungerar i JavaScript. Men istÀllet för att skapa strÀngar vid körtid skapar de nya typer baserade pÄ befintliga.
HÀr Àr ett grundlÀggande exempel:
type Greeting<T extends string> = `Hello, ${T}!`;
type MyGreeting = Greeting<"World">; // type MyGreeting = "Hello, World!"
I det hÀr exemplet Àr `Greeting` en template literal-typ som tar en strÀngtyp `T` som indata och returnerar en ny typ som Àr en sammanfogning av "Hello, ", `T` och "!".
GrundlÀggande strÀngmönstermatchning
Template literal-typer kan anvÀndas för att utföra grundlÀggande strÀngmönstermatchning. Detta lÄter dig skapa typer som endast Àr giltiga om de matchar ett visst mönster.
Till exempel kan du skapa en typ som endast accepterar strÀngar som börjar med "prefix-":
type PrefixedString<T extends string> = T extends `prefix-${string}` ? T : never;
type ValidPrefixedString = PrefixedString<"prefix-valid">; // type ValidPrefixedString = "prefix-valid"
type InvalidPrefixedString = PrefixedString<"invalid">; // type InvalidPrefixedString = never
I det hÀr exemplet anvÀnder `PrefixedString` en villkorlig typ för att kontrollera om indatastrÀngen `T` börjar med "prefix-". Om den gör det Àr typen `T` sjÀlv; annars Àr den `never`. `never` Àr en speciell typ i TypeScript som representerar typen av vÀrden som aldrig förekommer, vilket effektivt utesluter den ogiltiga strÀngen.
Extrahera delar av en strÀng
Template literal-typer kan ocksÄ anvÀndas för att extrahera delar av en strÀng. Detta Àr sÀrskilt anvÀndbart nÀr du behöver tolka data frÄn strÀngar och konvertera det till olika typer.
Anta att du har en strÀng som representerar en koordinat i formatet "x:10,y:20". Du kan anvÀnda template literal-typer för att extrahera x- och y-vÀrdena:
type CoordinateString = `x:${number},y:${number}`;
type ExtractX<T extends CoordinateString> = T extends `x:${infer X},y:${number}` ? X : never;
type ExtractY<T extends CoordinateString> = T extends `x:${number},y:${infer Y}` ? Y : never;
type XValue = ExtractX<"x:10,y:20">; // type XValue = 10
type YValue = ExtractY<"x:10,y:20">; // type YValue = 20
I det hÀr exemplet anvÀnder `ExtractX` och `ExtractY` nyckelordet `infer` för att fÄnga de delar av strÀngen som matchar typen `number`. `infer` lÄter dig extrahera en typ frÄn en mönstermatchning. De infÄngade typerna anvÀnds sedan som returtyp för den villkorliga typen.
Avancerad strÀngvalidering
Template literal-typer kan kombineras med andra TypeScript-funktioner, sÄsom union-typer och villkorliga typer, för att utföra avancerad strÀngvalidering. Detta lÄter dig skapa typer som upprÀtthÄller komplexa regler för strÀngars struktur och innehÄll.
Till exempel kan du skapa en typ som validerar ISO 8601-datumstrÀngar:
type Year = `${number}${number}${number}${number}`;
type Month = `0${number}` | `10` | `11` | `12`;
type Day = `${0}${number}` | `${1 | 2}${number}` | `30` | `31`;
type ISODate = `${Year}-${Month}-${Day}`;
type ValidDate = ISODate extends "2023-10-27" ? true : false; // true
type InvalidDate = ISODate extends "2023-13-27" ? true : false; // false
function processDate(date: ISODate) {
// Funktionslogik hÀr. TypeScript upprÀtthÄller ISODate-formatet.
return `Processing date: ${date}`;
}
console.log(processDate("2024-01-15")); // Fungerar
//console.log(processDate("2024-1-15")); // TypeScript-fel: Argumentet av typen '"2024-1-15"' kan inte tilldelas till parametern av typen '`${number}${number}${number}${number}-${0}${number}-${0}${number}` | `${number}${number}${number}${number}-${0}${number}-${1}${number}` | ... 14 till ... | `${number}${number}${number}${number}-12-31`'.
HÀr definieras `Year`, `Month` och `Day` med hjÀlp av template literal-typer för att representera de giltiga formaten för varje del av datumet. `ISODate` kombinerar sedan dessa typer för att skapa en typ som representerar en giltig ISO 8601-datumstrÀng. Exemplet visar ocksÄ hur denna typ kan anvÀndas för att upprÀtthÄlla dataformatering i en funktion, vilket förhindrar att felaktiga datumformat skickas in. Detta förbÀttrar kodens tillförlitlighet och förhindrar körtidsfel orsakade av ogiltig indata.
Verkliga anvÀndningsfall
Template literal-typer kan anvÀndas i en mÀngd olika verkliga scenarier. HÀr Àr nÄgra exempel:
- FormulÀrvalidering: Du kan anvÀnda template literal-typer för att validera formatet pÄ formulÀrindata, sÄsom e-postadresser, telefonnummer och postnummer.
- Validering av API-anrop: Du kan anvÀnda template literal-typer för att validera strukturen pÄ nyttolaster i API-anrop, och sÀkerstÀlla att de överensstÀmmer med det förvÀntade formatet. Till exempel validering av en valutakod (t.ex. "USD", "EUR", "GBP").
- Tolkning av konfigurationsfiler: Du kan anvÀnda template literal-typer för att tolka konfigurationsfiler och extrahera vÀrden baserat pÄ specifika mönster. TÀnk pÄ att validera filsökvÀgar i ett konfigurationsobjekt.
- StrÀngbaserade enums: Du kan skapa strÀngbaserade enums med validering med hjÀlp av template literal-typer.
Exempel: Validering av valutakoder
LÄt oss titta pÄ ett mer detaljerat exempel pÄ validering av valutakoder. Vi vill sÀkerstÀlla att endast giltiga ISO 4217-valutakoder anvÀnds i vÄr applikation. Dessa koder bestÄr vanligtvis av tre versaler.
type CurrencyCode = `${Uppercase<string>}${Uppercase<string>}${Uppercase<string>}`;
function formatCurrency(amount: number, currency: CurrencyCode) {
// Funktionslogik för att formatera valuta baserat pÄ den angivna koden.
return `$${amount} ${currency}`;
}
console.log(formatCurrency(100, "USD")); // Fungerar
//console.log(formatCurrency(100, "usd")); // TypeScript-fel: Argumentet av typen '"usd"' kan inte tilldelas till parametern av typen '`${Uppercase}${Uppercase}${Uppercase}`'.
//Mer exakt exempel:
type ValidCurrencyCode = "USD" | "EUR" | "GBP" | "JPY" | "CAD" | "AUD"; // Utöka vid behov
type StronglyTypedCurrencyCode = ValidCurrencyCode;
function formatCurrencyStronglyTyped(amount: number, currency: StronglyTypedCurrencyCode) {
return `$${amount} ${currency}`;
}
console.log(formatCurrencyStronglyTyped(100, "EUR")); // Fungerar
//console.log(formatCurrencyStronglyTyped(100, "CNY")); // TypeScript-fel: Argumentet av typen '"CNY"' kan inte tilldelas till parametern av typen '"USD" | "EUR" | "GBP" | "JPY" | "CAD" | "AUD"'.
Detta exempel visar hur man skapar en `CurrencyCode`-typ som endast accepterar strÀngar bestÄende av tre versaler. Det andra, mer starkt typade exemplet visar hur man kan begrÀnsa detta ytterligare till en fördefinierad lista av godkÀnda valutor.
Exempel: Validering av API-ÀndpunktssökvÀgar
Ett annat anvÀndningsfall Àr validering av API-ÀndpunktssökvÀgar. Du kan definiera en typ som representerar en giltig API-Àndpunktsstruktur, vilket sÀkerstÀller att anrop görs till korrekta sökvÀgar. Detta Àr sÀrskilt anvÀndbart i mikroservicearkitekturer dÀr flera tjÀnster kan exponera olika API:er.
type APIServiceName = "users" | "products" | "orders";
type APIEndpointPath = `/${APIServiceName}/${string}`;
function callAPI(path: APIEndpointPath) {
// Logik för API-anrop
console.log(`Calling API: ${path}`);
}
callAPI("/users/123"); // Giltig
callAPI("/products/details"); // Giltig
//callAPI("/invalid/path"); // TypeScript-fel
// Ănnu mer specifik:
type APIAction = "create" | "read" | "update" | "delete";
type APIEndpointPathSpecific = `/${APIServiceName}/${APIAction}`;
function callAPISpecific(path: APIEndpointPathSpecific) {
// Logik för API-anrop
console.log(`Calling specific API: ${path}`);
}
callAPISpecific("/users/create"); // Giltig
//callAPISpecific("/users/list"); // TypeScript-fel
Detta gör att du kan definiera strukturen för API-Àndpunkter mer exakt, vilket förhindrar stavfel och sÀkerstÀller konsekvens i hela din applikation. Detta Àr ett grundlÀggande exempel; mer komplexa mönster kan skapas för att validera frÄgeparametrar och andra delar av URL:en.
Fördelar med att anvÀnda Template Literal-typer
Att anvÀnda template literal-typer för strÀngmönstermatchning och validering erbjuder flera fördelar:
- FörbÀttrad typsÀkerhet: Template literal-typer lÄter dig upprÀtthÄlla striktare typbegrÀnsningar pÄ strÀngar, vilket minskar risken för körtidsfel.
- Ăkad lĂ€sbarhet i koden: Template literal-typer gör din kod mer lĂ€sbar genom att tydligt uttrycka det förvĂ€ntade formatet pĂ„ strĂ€ngar.
- Ăkad underhĂ„llbarhet: Template literal-typer gör din kod mer underhĂ„llbar genom att tillhandahĂ„lla en enda sanningskĂ€lla för strĂ€ngvalideringsregler.
- BÀttre utvecklarupplevelse: Template literal-typer ger bÀttre autofyll och felmeddelanden, vilket förbÀttrar den övergripande utvecklarupplevelsen.
BegrÀnsningar
Ăven om template literal-typer Ă€r kraftfulla har de ocksĂ„ vissa begrĂ€nsningar:
- Komplexitet: Template literal-typer kan bli komplexa, sÀrskilt nÀr man hanterar invecklade mönster. Det Àr avgörande att balansera fördelarna med typsÀkerhet mot kodens underhÄllbarhet.
- Prestanda: Template literal-typer kan pÄverka kompileringsprestandan, sÀrskilt i stora projekt. Detta beror pÄ att TypeScript behöver utföra mer komplex typkontroll.
- BegrĂ€nsat stöd för reguljĂ€ra uttryck: Ăven om template literal-typer möjliggör mönstermatchning, stöder de inte hela utbudet av funktioner i reguljĂ€ra uttryck. För mycket komplex strĂ€ngvalidering kan reguljĂ€ra uttryck vid körtid fortfarande behövas vid sidan av dessa typkonstruktioner för korrekt indatasanering.
BĂ€sta praxis
HÀr Àr nÄgra bÀsta praxis att tÀnka pÄ nÀr du anvÀnder template literal-typer:
- Börja enkelt: Börja med enkla mönster och öka gradvis komplexiteten vid behov.
- AnvÀnd beskrivande namn: AnvÀnd beskrivande namn för dina template literal-typer för att förbÀttra kodens lÀsbarhet.
- Dokumentera dina typer: Dokumentera dina template literal-typer för att förklara deras syfte och anvÀndning.
- Testa noggrant: Testa dina template literal-typer noggrant för att sÀkerstÀlla att de fungerar som förvÀntat.
- TÀnk pÄ prestandan: Var medveten om inverkan av template literal-typer pÄ kompileringsprestandan och optimera din kod dÀrefter.
Slutsats
Template literal-typer Ă€r en kraftfull funktion i TypeScript som lĂ„ter dig utföra avancerad strĂ€ngmanipulation, mönstermatchning och validering pĂ„ typnivĂ„. Genom att anvĂ€nda template literal-typer kan du skapa mer robusta, underhĂ„llbara och typsĂ€kra applikationer. Ăven om de har vissa begrĂ€nsningar vĂ€ger fördelarna med att anvĂ€nda template literal-typer ofta tyngre Ă€n nackdelarna, vilket gör dem till ett vĂ€rdefullt verktyg i varje TypeScript-utvecklares arsenal. I takt med att TypeScript-sprĂ„ket fortsĂ€tter att utvecklas kommer det att vara avgörande att förstĂ„ och anvĂ€nda dessa avancerade typfunktioner för att bygga högkvalitativ programvara. Kom ihĂ„g att balansera komplexitet med lĂ€sbarhet och att alltid prioritera noggrann testning.