Utforsk TypeScript sine kraftige template literal typer for avansert strengmanipulering, mønstersammenligning og validering. Lær med praktiske eksempler og brukstilfeller.
Template Literal Typer: Strengmønstersammenligning og Validering i TypeScript
TypeScripts typesystem er i konstant utvikling, og tilbyr utviklere kraftigere verktøy for å uttrykke kompleks logikk og sikre typesikkerhet. En av de mest interessante og allsidige funksjonene som er introdusert i nyere versjoner er template literal typer. Disse typene lar deg manipulere strenger på typenivå, og muliggjør avansert strengmønstersammenligning og validering. Dette åpner for en helt ny verden av muligheter for å lage mer robuste og vedlikeholdbare applikasjoner.
Hva er Template Literal Typer?
Template literal typer er en form for type som er konstruert ved å kombinere streng literal typer og unionstyper, likt hvordan template literals fungerer i JavaScript. Men i stedet for å lage kjøretidsstrenger, lager de nye typer basert på eksisterende.
Her er et grunnleggende eksempel:
type Greeting<T extends string> = `Hello, ${T}!`;
type MyGreeting = Greeting<"World">; // type MyGreeting = "Hello, World!"
I dette eksemplet er `Greeting` en template literal type som tar en strengtype `T` som input og returnerer en ny type som er sammenkoblingen av "Hello, ", `T`, og "!".
Grunnleggende Strengmønstersammenligning
Template literal typer kan brukes til å utføre grunnleggende strengmønstersammenligning. Dette lar deg lage typer som bare er gyldige hvis de samsvarer med et bestemt mønster.
For eksempel kan du lage en type som bare aksepterer strenger som starter 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 dette eksemplet bruker `PrefixedString` en betinget type for å sjekke om input-strengen `T` starter med "prefix-". Hvis den gjør det, er typen `T` selv; ellers er det `never`. `never` er en spesiell type i TypeScript som representerer typen verdier som aldri forekommer, og effektivt ekskluderer den ugyldige strengen.
Ekstrahere Deler av en Streng
Template literal typer kan også brukes til å trekke ut deler av en streng. Dette er spesielt nyttig når du trenger å parse data fra strenger og konvertere den til forskjellige typer.
La oss si at du har en streng som representerer en koordinat i formatet "x:10,y:20". Du kan bruke template literal typer til å trekke ut x- og y-verdiene:
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 dette eksemplet bruker `ExtractX` og `ExtractY` nøkkelordet `infer` for å fange opp delene av strengen som samsvarer med `number`-typen. `infer` lar deg trekke ut en type fra en mønstersammenligning. De fangede typene brukes deretter som returtype for den betingede typen.
Avansert Strengvalidering
Template literal typer kan kombineres med andre TypeScript-funksjoner, for eksempel unionstyper og betingede typer, for å utføre avansert strengvalidering. Dette lar deg lage typer som håndhever komplekse regler for strukturen og innholdet i strenger.
For eksempel kan du lage en type som validerer ISO 8601 datostrenger:
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) {
// Funksjonslogikk her. TypeScript håndhever ISODate-formatet.
return `Behandler dato: ${date}`;
}
console.log(processDate("2024-01-15")); // Fungerer
//console.log(processDate("2024-1-15")); // TypeScript feil: Argument av type '"2024-1-15"' kan ikke tilordnes parameter av type '`${number}${number}${number}${number}-${0}${number}-${0}${number}` | `${number}${number}${number}${number}-${0}${number}-${1}${number}` | ... 14 flere ... | `${number}${number}${number}${number}-12-31`'.
Her er `Year`, `Month` og `Day` definert ved hjelp av template literal typer for å representere de gyldige formatene for hver del av datoen. `ISODate` kombinerer deretter disse typene for å lage en type som representerer en gyldig ISO 8601 datostreng. Eksemplet demonstrerer også hvordan denne typen kan brukes til å håndheve dataformatering i en funksjon, og forhindre at feil datoformater sendes. Dette forbedrer kodepåliteligheten og forhindrer kjøretidsfeil forårsaket av ugyldig input.
Brukstilfeller i den Virkelige Verden
Template literal typer kan brukes i en rekke scenarier i den virkelige verden. Her er noen eksempler:
- Skjemavalidering: Du kan bruke template literal typer til å validere formatet på skjema input, for eksempel e-postadresser, telefonnumre og postnummer.
- API-forespørselsvalidering: Du kan bruke template literal typer til å validere strukturen til API-forespørsels nyttelaster, og sikre at de samsvarer med forventet format. For eksempel, validere en valutakode (f.eks. "USD", "EUR", "GBP").
- Konfigurasjonsfilparsing: Du kan bruke template literal typer til å parse konfigurasjonsfiler og trekke ut verdier basert på spesifikke mønstre. Vurder å validere filbaner i et konfigurasjonsobjekt.
- Strengbaserte Enums: Du kan lage strengbaserte enums med validering ved hjelp av template literal typer.
Eksempel: Validere Valutakoder
La oss se på et mer detaljert eksempel på å validere valutakoder. Vi vil sikre at bare gyldige ISO 4217 valutakoder brukes i applikasjonen vår. Disse kodene er vanligvis tre store bokstaver.
type CurrencyCode = `${Uppercase<string>}${Uppercase<string>}${Uppercase<string>}`;
function formatCurrency(amount: number, currency: CurrencyCode) {
// Funksjonslogikk for å formatere valuta basert på den angitte koden.
return `$${amount} ${currency}`;
}
console.log(formatCurrency(100, "USD")); // Fungerer
//console.log(formatCurrency(100, "usd")); // TypeScript feil: Argument av type '"usd"' kan ikke tilordnes parameter av type '`${Uppercase}${Uppercase}${Uppercase}`'.
//Mer presist eksempel:
type ValidCurrencyCode = "USD" | "EUR" | "GBP" | "JPY" | "CAD" | "AUD"; // Utvid etter behov
type StronglyTypedCurrencyCode = ValidCurrencyCode;
function formatCurrencyStronglyTyped(amount: number, currency: StronglyTypedCurrencyCode) {
return `$${amount} ${currency}`;
}
console.log(formatCurrencyStronglyTyped(100, "EUR")); // Fungerer
//console.log(formatCurrencyStronglyTyped(100, "CNY")); // TypeScript feil: Argument av type '"CNY"' kan ikke tilordnes parameter av type '"USD" | "EUR" | "GBP" | "JPY" | "CAD" | "AUD"'.
Dette eksemplet demonstrerer hvordan du oppretter en `CurrencyCode`-type som bare aksepterer strenger som består av tre store bokstaver. Det andre, mer sterkt typede eksemplet viser hvordan du kan begrense dette enda mer til en forhåndsdefinert liste over akseptable valutaer.
Eksempel: Validere API Endepunkt Baner
Et annet brukstilfelle er å validere API endepunkt baner. Du kan definere en type som representerer en gyldig API endepunkt struktur, og sikre at forespørsler gjøres til riktige baner. Dette er spesielt nyttig i mikroservicearkitekturer der flere tjenester kan eksponere forskjellige APIer.
type APIServiceName = "users" | "products" | "orders";
type APIEndpointPath = `/${APIServiceName}/${string}`;
function callAPI(path: APIEndpointPath) {
// API kalllogikk
console.log(`Kaller API: ${path}`);
}
callAPI("/users/123"); // Gyldig
callAPI("/products/details"); // Gyldig
//callAPI("/invalid/path"); // TypeScript feil
// Enda mer spesifikt:
type APIAction = "create" | "read" | "update" | "delete";
type APIEndpointPathSpecific = `/${APIServiceName}/${APIAction}`;
function callAPISpecific(path: APIEndpointPathSpecific) {
// API kalllogikk
console.log(`Kaller spesifikk API: ${path}`);
}
callAPISpecific("/users/create"); // Gyldig
//callAPISpecific("/users/list"); // TypeScript feil
Dette lar deg definere strukturen til API endepunkter mer presist, og forhindre skrivefeil og sikre konsistens på tvers av applikasjonen din. Dette er et grunnleggende eksempel; mer komplekse mønstre kan opprettes for å validere spørringsparametere og andre deler av URLen.
Fordeler med å Bruke Template Literal Typer
Å bruke template literal typer for strengmønstersammenligning og validering gir flere fordeler:
- Forbedret Typesikkerhet: Template literal typer lar deg håndheve strengere typebegrensninger på strenger, noe som reduserer risikoen for kjøretidsfeil.
- Forbedret Kode Lesbarhet: Template literal typer gjør koden din mer lesbar ved å tydelig uttrykke det forventede formatet på strenger.
- Økt Vedlikeholdbarhet: Template literal typer gjør koden din mer vedlikeholdbar ved å tilby en enkelt kilde til sannhet for strengvalideringsregler.
- Bedre Utvikleropplevelse: Template literal typer gir bedre autofullføring og feilmeldinger, noe som forbedrer den totale utvikleropplevelsen.
Begrensninger
Mens template literal typer er kraftige, har de også noen begrensninger:
- Kompleksitet: Template literal typer kan bli komplekse, spesielt når man arbeider med intrikate mønstre. Det er avgjørende å balansere fordelene med typesikkerhet med kodevedlikeholdbarhet.
- Ytelse: Template literal typer kan påvirke kompilering ytelse, spesielt i store prosjekter. Dette er fordi TypeScript trenger å utføre mer kompleks typekontroll.
- Begrenset Støtte for Regulære Uttrykk: Mens template literal typer tillater mønstersammenligning, støtter de ikke hele spekteret av regulære uttrykksfunksjoner. For svært kompleks strengvalidering kan regulære uttrykk ved kjøretid fortsatt være nødvendig sammen med disse typekonstruksjonene for riktig input sanitering.
Beste Praksis
Her er noen beste praksis å huske på når du bruker template literal typer:
- Start Enkelt: Begynn med enkle mønstre og øk gradvis kompleksiteten etter behov.
- Bruk Beskrivende Navn: Bruk beskrivende navn for dine template literal typer for å forbedre kode lesbarheten.
- Dokumenter Typene Dine: Dokumenter dine template literal typer for å forklare deres formål og bruk.
- Test Grundig: Test dine template literal typer grundig for å sikre at de oppfører seg som forventet.
- Vurder Ytelse: Vær oppmerksom på virkningen av template literal typer på kompilering ytelse og optimaliser koden din deretter.
Konklusjon
Template literal typer er en kraftig funksjon i TypeScript som lar deg utføre avansert strengmanipulering, mønstersammenligning og validering på typenivå. Ved å bruke template literal typer kan du lage mer robuste, vedlikeholdbare og typesikre applikasjoner. Selv om de har noen begrensninger, oppveier fordelene ved å bruke template literal typer ofte ulempene, noe som gjør dem til et verdifullt verktøy i enhver TypeScript-utviklers arsenal. Etter hvert som TypeScript-språket fortsetter å utvikle seg, vil det være avgjørende å forstå og bruke disse avanserte typefunksjonene for å bygge programvare av høy kvalitet. Husk å balansere kompleksitet med lesbarhet og alltid prioritere grundig testing.