Utforska avancerade TypeScript-tekniker med template literals för kraftfull manipulering av strÀngtyper. LÀr dig att parsa, transformera och validera strÀngbaserade typer.
Parsning av TypeScript Template Literals: Avancerad manipulering av strÀngtyper
TypeScript's typsystem erbjuder kraftfulla verktyg för att manipulera och validera data vid kompileringstid. Bland dessa verktyg erbjuder template literals ett unikt sÀtt att hantera strÀngtyper. Denna artikel fördjupar sig i de avancerade aspekterna av parsning av template literals och visar hur man kan skapa sofistikerad logik pÄ typnivÄ för strÀngbaserad data.
Vad Àr Template Literal-typer?
Template literal-typer, som introducerades i TypeScript 4.1, lÄter dig definiera strÀngtyper baserade pÄ strÀngliteraler och andra typer. De anvÀnder backticks (`) för att definiera typen, liknande template literals i JavaScript.
Till exempel:
type Color = "red" | "green" | "blue";
type Shade = "light" | "dark";
type ColorCombination = `${Shade} ${Color}`;
// ColorCombination Àr nu "light red" | "light green" | "light blue" | "dark red" | "dark green" | "dark blue"
Denna till synes enkla funktion öppnar upp för en mÀngd möjligheter för strÀngbearbetning vid kompileringstid.
GrundlÀggande anvÀndning av Template Literal-typer
Innan vi dyker in i avancerade tekniker, lÄt oss granska nÄgra grundlÀggande anvÀndningsfall.
Sammanfoga strÀngliteraler
Du kan enkelt kombinera strÀngliteraler och andra typer för att skapa nya strÀngtyper:
type Greeting = `Hello, ${string}!`;
// ExempelanvÀndning
const greet = (name: string): Greeting => `Hello, ${name}!`;
const message: Greeting = greet("World"); // Giltig
const invalidMessage: Greeting = "Goodbye, World!"; // Fel: Typen '"Goodbye, World!"' kan inte tilldelas typen '`Hello, ${string}!`'.
AnvÀnda union-typer
Union-typer lÄter dig definiera en typ som en kombination av flera möjliga vÀrden. Template literals kan inkludera union-typer för att generera mer komplexa unioner av strÀngtyper:
type HTTPMethod = "GET" | "POST" | "PUT" | "DELETE";
type Endpoint = `/api/users` | `/api/products`;
type Route = `${HTTPMethod} ${Endpoint}`;
// Route Àr nu "GET /api/users" | "POST /api/users" | "PUT /api/users" | "DELETE /api/users" | "GET /api/products" | "POST /api/products" | "PUT /api/products" | "DELETE /api/products"
Avancerade tekniker för parsning av Template Literals
Den verkliga kraften i template literal-typer ligger i deras förmÄga att kombineras med andra avancerade TypeScript-funktioner, sÄsom villkorliga typer och typinferens, för att parsa och manipulera strÀngtyper.
HÀrleda delar av en strÀngtyp
Du kan anvÀnda nyckelordet infer inom en villkorlig typ för att extrahera specifika delar av en strÀngtyp. Detta Àr grunden för att parsa strÀngtyper.
TÀnk dig en typ som extraherar filÀndelsen frÄn ett filnamn:
type GetFileExtension = T extends `${string}.${infer Extension}` ? Extension : never;
// Exempel
type Extension1 = GetFileExtension<"myFile.txt">; // "txt"
type Extension2 = GetFileExtension<"anotherFile.image.jpg">; // "image.jpg" (tar den sista Àndelsen)
type Extension3 = GetFileExtension<"noExtension">; // never
I detta exempel kontrollerar den villkorliga typen om indatatypen T matchar mönstret ${string}.${infer Extension}. Om den gör det, hÀrleds delen efter den sista punkten till typvariabeln Extension, som sedan returneras. Annars returnerar den never.
Parsning med flera inferenser
Du kan anvÀnda flerainfer-nyckelord i samma template literal för att extrahera flera delar av en strÀngtyp samtidigt.
type ParseConnectionString =
T extends `${infer Protocol}://${infer Host}:${infer Port}` ?
{ protocol: Protocol, host: Host, port: Port } : never;
// Exempel
type Connection = ParseConnectionString<"http://localhost:3000">;
// { protocol: "http", host: "localhost", port: "3000" }
type InvalidConnection = ParseConnectionString<"invalid-connection">; // never
Denna typ parsar en anslutningsstrÀng till dess komponenter: protokoll, vÀrd och port.
Rekursiva typdefinitioner för komplex parsning
För mer komplexa strÀngstrukturer kan du anvÀnda rekursiva typdefinitioner. Detta lÄter dig upprepade gÄnger parsa delar av en strÀngtyp tills du nÄr ett önskat resultat.
LÄt oss sÀga att du vill dela upp en strÀng i en array av enskilda tecken pÄ typnivÄ. Detta Àr betydligt mer avancerat.
type StringToArray =
T extends `${infer Char}${infer Rest}`
? StringToArray
: Acc;
// Exempel
type MyArray = StringToArray<"hello">; // ["h", "e", "l", "l", "o"]
Förklaring:
StringToArray<T extends string, Acc extends string[] = []>: Detta definierar en generisk typ med namnetStringToArraysom tar en strÀngtypTsom indata och en valfri ackumulatorAccsom standard Àr en tom strÀngarray. Ackumulatorn kommer att lagra tecknen medan vi bearbetar dem.T extends `${infer Char}${infer Rest}`: Detta Àr den villkorliga typkontrollen. Den kontrollerar om indatastrÀngenTkan delas upp i ett första teckenCharoch den ÄterstÄende strÀngenRest. NyckelordetinferanvÀnds för att fÄnga dessa delar.StringToArray<Rest, [...Acc, Char]>: Om uppdelningen lyckas, anropar vi rekursivtStringToArraymedRestav strÀngen och en ny ackumulator. Den nya ackumulatorn skapas genom att sprida den befintligaAccoch lÀgga till det nuvarande tecknetChari slutet. Detta lÀgger effektivt till tecknet i den ackumulerande arrayen.Acc: Om strÀngen Àr tom (den villkorliga typen misslyckas, vilket betyder att det inte finns fler tecken), returnerar vi den ackumulerade arrayenAcc.
Detta exempel demonstrerar kraften i rekursion för att manipulera strÀngtyper. Varje rekursivt anrop skalar av ett tecken och lÀgger till det i arrayen tills strÀngen Àr tom.
Arbeta med avgrÀnsare
Template literals kan enkelt anvÀndas med avgrÀnsare för att parsa strÀngar. LÄt oss sÀga att du vill extrahera ord som Àr separerade med kommatecken.
type SplitString =
T extends `${infer First}${D}${infer Rest}`
? [First, ...SplitString]
: [T];
// Exempel
type Words = SplitString<"apple,banana,cherry", ",">; // ["apple", "banana", "cherry"]
Denna typ delar rekursivt upp strÀngen vid varje förekomst av avgrÀnsaren D.
Praktiska tillÀmpningar
Dessa avancerade tekniker för parsning av template literals har mÄnga praktiska tillÀmpningar i TypeScript-projekt.
Datavalidering
Du kan validera strÀngbaserad data mot specifika mönster vid kompileringstid. Till exempel validering av e-postadresser, telefonnummer eller kreditkortsnummer. Detta tillvÀgagÄngssÀtt ger tidig Äterkoppling och minskar körtidsfel.
HÀr Àr ett exempel pÄ validering av ett förenklat e-postadressformat:
type EmailFormat = `${string}@${string}.${string}`;
const validateEmail = (email: string): email is EmailFormat => {
// I verkligheten skulle ett mycket mer komplext regex anvÀndas för korrekt e-postvalidering.
// Detta Àr endast i demonstrationssyfte.
return /.+@.+\..+/.test(email);
}
const validEmail: EmailFormat = "user@example.com"; // Giltig
const invalidEmail: EmailFormat = "invalid-email"; // Typen 'string' kan inte tilldelas typen '`${string}@${string}.${string}`'.
if(validateEmail(validEmail)) {
console.log("Giltig e-post");
}
if(validateEmail("invalid-email")) {
console.log("Detta kommer inte att skrivas ut.");
}
Ăven om körtidsvalidering med ett regex fortfarande Ă€r nödvĂ€ndigt i fall dĂ€r typkontrollen inte helt kan upprĂ€tthĂ„lla begrĂ€nsningen (t.ex. vid hantering av extern indata), ger EmailFormat-typen ett vĂ€rdefullt första försvarslinje vid kompileringstid.
Generering av API-slutpunkter
Template literals kan anvÀndas för att generera typer för API-slutpunkter baserat pÄ en bas-URL och en uppsÀttning parametrar. Detta kan hjÀlpa till att sÀkerstÀlla konsekvens och typsÀkerhet nÀr man arbetar med API:er.
type BaseURL = "https://api.example.com";
type Resource = "users" | "products";
type ID = string | number;
type GetEndpoint = `${BaseURL}/${T}/${U}`;
// Exempel
type UserEndpoint = GetEndpoint<"users", 123>; // "https://api.example.com/users/123"
type ProductEndpoint = GetEndpoint<"products", "abc-456">; // "https://api.example.com/products/abc-456"
Kodgenerering
I mer avancerade scenarier kan template literal-typer anvÀndas som en del av kodgenereringsprocesser. Till exempel för att generera SQL-frÄgor baserat pÄ ett schema eller skapa UI-komponenter baserat pÄ en konfigurationsfil.
Internationalisering (i18n)
Template literals kan vara vÀrdefulla i i18n-scenarier. TÀnk dig till exempel ett system dÀr översÀttningsnycklar följer en specifik namngivningskonvention:
type SupportedLanguages = 'en' | 'es' | 'fr';
type TranslationKeyPrefix = 'common' | 'product' | 'checkout';
type TranslationKey = `${TPrefix}.${string}`;
// ExempelanvÀndning:
const getTranslation = (key: TranslationKey, lang: SupportedLanguages): string => {
// Simulera hÀmtning av översÀttningen frÄn en resursbunt baserat pÄ nyckel och sprÄk
const translations: Record> = {
'common.greeting': {
en: 'Hello',
es: 'Hola',
fr: 'Bonjour',
},
'product.description': {
en: 'A fantastic product!',
es: 'ÂĄUn producto fantĂĄstico!',
fr: 'Un produit fantastique !',
},
};
const translation = translations[key]?.[lang];
return translation || `Translation not found for key: ${key} in language: ${lang}`;
};
const englishGreeting = getTranslation('common.greeting', 'en'); // Hello
const spanishDescription = getTranslation('product.description', 'es'); // ÂĄUn producto fantĂĄstico!
const unknownTranslation = getTranslation('nonexistent.key' as TranslationKey, 'en'); // Translation not found for key: nonexistent.key in language: en
Typen TranslationKey sÀkerstÀller att alla översÀttningsnycklar följer ett konsekvent format, vilket förenklar processen att hantera översÀttningar och förhindra fel.
BegrÀnsningar
Ăven om template literal-typer Ă€r kraftfulla har de ocksĂ„ sina begrĂ€nsningar:
- Komplexitet: Komplex parsningslogik kan snabbt bli svÄr att lÀsa och underhÄlla.
- Prestanda: Omfattande anvÀndning av template literal-typer kan pÄverka prestandan vid kompilering, sÀrskilt i stora projekt.
- Luckor i typsÀkerheten: Som demonstrerats i exemplet med e-postvalidering Àr kontroller vid kompileringstid ibland inte tillrÀckliga. Körtidsvalidering behövs fortfarande för fall dÀr extern data mÄste följa strikta format.
BĂ€sta praxis
För att effektivt anvÀnda template literal-typer, följ dessa bÀsta praxis:
- HÄll det enkelt: Bryt ner komplex parsningslogik i mindre, hanterbara typer.
- Dokumentera dina typer: Dokumentera tydligt syftet och anvÀndningen av dina template literal-typer.
- Testa dina typer: Skapa enhetstester för att sÀkerstÀlla att dina typer beter sig som förvÀntat.
- Balansera validering vid kompilering och körtid: AnvÀnd template literal-typer för grundlÀggande validering och körtidskontroller för mer komplexa scenarier.
Slutsats
TypeScript template literal-typer erbjuder ett kraftfullt och flexibelt sĂ€tt att manipulera strĂ€ngtyper vid kompileringstid. Genom att kombinera template literals med villkorliga typer och typinferens kan du skapa sofistikerad logik pĂ„ typnivĂ„ för att parsa, validera och transformera strĂ€ngbaserad data. Ăven om det finns begrĂ€nsningar att ta hĂ€nsyn till, kan fördelarna med att anvĂ€nda template literal-typer nĂ€r det gĂ€ller typsĂ€kerhet och kodunderhĂ„ll vara betydande.
Genom att bemÀstra dessa avancerade tekniker kan utvecklare skapa mer robusta och pÄlitliga TypeScript-applikationer.
Vidare utforskning
För att fördjupa din förstÄelse av template literal-typer, övervÀg att utforska följande Àmnen:
- Mapped Types: LÀr dig hur du transformerar objekttyper baserat pÄ template literal-typer.
- Utility-typer: Utforska inbyggda TypeScript utility-typer som kan anvÀndas tillsammans med template literal-typer.
- Avancerade villkorliga typer: Dyk djupare in i funktionerna hos villkorliga typer för mer komplex logik pÄ typnivÄ.