Verken TypeScript Template Literal Types en bouw een runtime validatie-engine voor robuuste stringverificatie en typeveiligheid. Leer hoe u fouten voorkomt door strings tijdens runtime te valideren aan de hand van uw gedefinieerde template literal types.
TypeScript Template Literal Validatie-engine: Runtime Stringverificatie
TypeScript's template literal types bieden krachtige compile-time stringmanipulatie en typeveiligheid. Deze controles zijn echter beperkt tot de compilatietijd. Dit blogartikel onderzoekt hoe je een runtime validatie-engine bouwt voor TypeScript template literal types, wat robuuste stringverificatie mogelijk maakt en potentiƫle fouten tijdens de uitvoering van het programma voorkomt.
Introductie tot TypeScript Template Literal Types
Template literal types stellen u in staat om specifieke stringvormen te definiƫren op basis van letterlijke waarden, unions en type-inferentie. Dit maakt precieze typecontrole en automatisch aanvullen mogelijk, wat vooral handig is bij het omgaan met gestructureerde gegevens of domeinspecifieke talen.
Neem bijvoorbeeld een type voor het representeren van valutacodes:
type CurrencyCode = "USD" | "EUR" | "GBP";
type FormattedCurrencyString = `${CurrencyCode}-${number}`;
const validCurrency: FormattedCurrencyString = "USD-100"; // OK
const invalidCurrency: FormattedCurrencyString = "CAD-50"; // Typefout tijdens compilatie
Dit voorbeeld laat zien hoe TypeScript het FormattedCurrencyString type afdwingt tijdens de compilatie. Als de valutacode echter afkomstig is van een externe bron (bijv. gebruikersinvoer, API-respons), hebt u runtime validatie nodig om de typeveiligheid te garanderen.
De Noodzaak van Runtime Validatie
Hoewel TypeScript uitstekende compile-time typecontrole biedt, kan het de geldigheid van gegevens die tijdens runtime van externe bronnen worden ontvangen niet garanderen. Uitsluitend vertrouwen op compile-time types kan leiden tot onverwachte fouten en kwetsbaarheden.
Overweeg het volgende scenario:
function processCurrency(currencyString: FormattedCurrencyString) {
// ... enige logica die ervan uitgaat dat de string correct is geformatteerd
}
const userInput = "CAD-50"; // Neem aan dat dit van gebruikersinvoer komt
// Dit zal compileren, maar zal een runtime-fout veroorzaken als de logica binnen
// `processCurrency` afhankelijk is van het format.
processCurrency(userInput as FormattedCurrencyString);
In dit geval 'casten' we userInput naar FormattedCurrencyString, waarmee we de compile-time controles van TypeScript omzeilen. Als processCurrency ervan uitgaat dat de string correct is geformatteerd, zal er een runtime-fout optreden.
Runtime validatie overbrugt deze kloof door te verifiƫren dat de gegevens die tijdens runtime worden ontvangen, voldoen aan de verwachte TypeScript-types.
Een Template Literal Validatie-engine Bouwen
We kunnen een runtime validatie-engine bouwen met behulp van reguliere expressies en het typesysteem van TypeScript. De engine neemt een template literal type en een string als invoer en retourneert of de string overeenkomt met het type.
Stap 1: Een Type Definiƫren voor Runtime Validatie
Eerst hebben we een generiek type nodig dat het runtime-equivalent van een template literal type kan representeren. Dit type moet verschillende soorten template literals kunnen verwerken, inclusief literals, unions en type parameters.
type TemplateLiteralToRegex =
T extends `${infer Start}${infer Middle}${infer End}`
? Start extends string
? Middle extends string
? End extends string
? TemplateLiteralToRegexStart & TemplateLiteralToRegexMiddle & TemplateLiteralToRegex
: never
: never
: never
: TemplateLiteralToRegexStart;
type TemplateLiteralToRegexStart = T extends `${infer Literal}` ? Literal : string;
type TemplateLiteralToRegexMiddle = T extends `${infer Literal}` ? Literal : string;
Deze recursieve typedefinitie breekt de template literal op in zijn samenstellende delen en zet elk deel om in een patroon voor een reguliere expressie.
Stap 2: De Validatiefunctie Implementeren
Vervolgens implementeren we de validatiefunctie die het template literal type en de te valideren string als invoer neemt. Deze functie gebruikt de reguliere expressie die door TemplateLiteralToRegex wordt gegenereerd om de string te testen.
function isValid(str: string, templateType: T): boolean {
const regexPattern = `^${convertTemplateLiteralToRegex(templateType)}$`;
const regex = new RegExp(regexPattern);
return regex.test(str);
}
function convertTemplateLiteralToRegex(templateType: T): string {
// Basisconversie voor letterlijke strings - breid dit uit voor complexere scenario's
return templateType.replace(/[.*+?^${}()|[\]]/g, '\\$&'); // Escape speciale regex-tekens
}
Deze functie escapet speciale tekens voor reguliere expressies en creƫert een reguliere expressie van het template literal type, en test vervolgens de string aan de hand van die reguliere expressie.
Stap 3: De Validatie-engine Gebruiken
Nu kunt u de isValid-functie gebruiken om strings tijdens runtime te valideren aan de hand van uw template literal types.
type CurrencyCode = "USD" | "EUR" | "GBP";
type FormattedCurrencyString = `${CurrencyCode}-${number}`;
const userInput1 = "USD-100";
const userInput2 = "CAD-50";
console.log(`'${userInput1}' is geldig: ${isValid(userInput1, "USD-100" )}`); // true
console.log(`'${userInput2}' is geldig: ${isValid(userInput2, "USD-100")}`); // false
console.log(`'${userInput1}' is geldig: ${isValid(userInput1, `USD-${100}`)}`); // true
console.log(`'${userInput2}' is geldig: ${isValid(userInput2, `USD-${100}`)}`); // false
Dit voorbeeld laat zien hoe u de isValid-functie kunt gebruiken om gebruikersinvoer te valideren ten opzichte van het FormattedCurrencyString type. De uitvoer zal tonen of de invoerstrings als geldig worden beschouwd of niet, gebaseerd op de gespecificeerde template literal.
Geavanceerde Validatiescenario's
De basis validatie-engine kan worden uitgebreid om complexere scenario's aan te kunnen, zoals unions, conditionele types en recursieve types.
Omgaan met Unions
Om met unions om te gaan, kunt u het TemplateLiteralToRegex type aanpassen om een reguliere expressie te genereren die overeenkomt met een van de union-leden.
type CurrencyCode = "USD" | "EUR" | "GBP";
type FormattedCurrencyString = `${CurrencyCode}-${number}`;
function isValidCurrencyCode(str: string, templateType: T): boolean {
const currencyCodes: CurrencyCode[] = ["USD", "EUR", "GBP"];
return currencyCodes.includes(str as CurrencyCode);
}
function isValidUnionFormattedCurrencyString(str: string): boolean {
const parts = str.split('-');
if(parts.length !== 2) return false;
const [currencyCode, amount] = parts;
if (!isValidCurrencyCode(currencyCode, currencyCode)) return false;
if (isNaN(Number(amount))) return false;
return true;
}
console.log(`'USD-100' is een geldige geformatteerde string: ${isValidUnionFormattedCurrencyString('USD-100')}`);
console.log(`'CAD-50' is een geldige geformatteerde string: ${isValidUnionFormattedCurrencyString('CAD-50')}`);
Omgaan met Conditionele Types
Conditionele types kunnen worden afgehandeld door de voorwaarde tijdens runtime te evalueren en verschillende reguliere expressies te genereren op basis van het resultaat.
type IsString = T extends string ? true : false;
// Dit voorbeeld vereist meer geavanceerde logica en is niet volledig implementeerbaar met eenvoudige regex.
// Runtime type guards bieden een robuustere oplossing in dit specifieke scenario.
// De onderstaande code is illustratief en zou aanpassing behoeven om complexe conditionele types te verwerken.
function isString(value: any): value is string {
return typeof value === 'string';
}
function isValidConditionalType(value: any): boolean {
return isString(value);
}
console.log(`'hello' is een string: ${isValidConditionalType('hello')}`);
console.log(`123 is een string: ${isValidConditionalType(123)}`);
Omgaan met Recursieve Types
Recursieve types kunnen worden afgehandeld door een recursieve functie te definiƫren die het patroon voor de reguliere expressie genereert. Wees echter voorzichtig om oneindige recursie en stack overflow-fouten te vermijden. Voor diepe recursie zijn iteratieve benaderingen met passende limieten cruciaal.
Alternatieven voor Reguliere Expressies
Hoewel reguliere expressies een krachtig hulpmiddel zijn voor stringvalidatie, kunnen ze complex en moeilijk te onderhouden zijn. Andere benaderingen voor runtime validatie zijn onder meer:
- Aangepaste Validatiefuncties: Schrijf aangepaste functies om specifieke types te valideren op basis van de vereisten van uw applicatie.
- Type Guards: Gebruik type guards om het type van een variabele tijdens runtime te verfijnen.
- Validatiebibliotheken: Maak gebruik van bestaande validatiebibliotheken zoals Zod of Yup om het validatieproces te vereenvoudigen.
Zod biedt bijvoorbeeld een op schema's gebaseerde declaratie die wordt vertaald naar een validatie-runtime:
import { z } from 'zod';
const CurrencyCodeSchema = z.enum(['USD', 'EUR', 'GBP']);
const FormattedCurrencyStringSchema = z.string().regex(new RegExp(`^${CurrencyCodeSchema.enum.USD}|${CurrencyCodeSchema.enum.EUR}|${CurrencyCodeSchema.enum.GBP}-[0-9]+$`));
try {
const validCurrency = FormattedCurrencyStringSchema.parse("USD-100");
console.log("Geldige Valuta:", validCurrency);
} catch (error) {
console.error("Ongeldige Valuta:", error);
}
try {
const invalidCurrency = FormattedCurrencyStringSchema.parse("CAD-50");
console.log("Geldige Valuta:", invalidCurrency); //Dit wordt niet uitgevoerd als parse mislukt.
} catch (error) {
console.error("Ongeldige Valuta:", error);
}
Best Practices voor Runtime Validatie
Houd bij het implementeren van runtime validatie de volgende best practices in gedachten:
- Valideer aan de Grens: Valideer gegevens zodra ze uw systeem binnenkomen (bijv. gebruikersinvoer, API-responsen).
- Geef Duidelijke Foutmeldingen: Genereer informatieve foutmeldingen om gebruikers te helpen begrijpen waarom hun invoer ongeldig is.
- Gebruik een Consistente Validatiestrategie: Hanteer een consistente validatiestrategie in uw hele applicatie om de data-integriteit te waarborgen.
- Test Uw Validatielogica: Test uw validatielogica grondig om ervoor te zorgen dat deze correct geldige en ongeldige gegevens identificeert.
- Balanceer Prestaties en Veiligheid: Optimaliseer uw validatielogica voor prestaties en zorg er tegelijkertijd voor dat deze effectief beveiligingsproblemen voorkomt. Vermijd te complexe regex die tot denial-of-service kan leiden.
Overwegingen voor Internationalisering
Bij het omgaan met stringvalidatie in een wereldwijde context moet u rekening houden met internationalisering (i18n) en lokalisatie (l10n). Verschillende locales kunnen verschillende regels hebben voor het formatteren van strings, zoals datums, getallen en valutawaarden.
Het valutasymbool voor de Euro (ā¬) kan bijvoorbeeld voor of na het bedrag verschijnen, afhankelijk van de locale. Op dezelfde manier kan het decimaalteken een punt (.) of een komma (,) zijn.
Om deze variaties aan te kunnen, kunt u internationaliseringsbibliotheken zoals Intl gebruiken, die API's bieden voor het formatteren en parsen van locale-gevoelige gegevens. U zou bijvoorbeeld het vorige voorbeeld kunnen aanpassen om verschillende valuta-indelingen te hanteren:
function isValidCurrencyString(currencyString: string, locale: string): boolean {
try {
const formatter = new Intl.NumberFormat(locale, { style: 'currency', currency: currencyString.substring(0,3) }); //Zeer basis voorbeeld
//Probeer de valuta te parsen met de formatter. Dit voorbeeld is opzettelijk zeer eenvoudig.
return true;
} catch (error) {
return false;
}
}
console.log(`USD-100 is geldig voor en-US: ${isValidCurrencyString('USD-100', 'en-US')}`);
console.log(`EUR-100 is geldig voor fr-FR: ${isValidCurrencyString('EUR-100', 'fr-FR')}`);
Dit codefragment biedt een fundamenteel voorbeeld. Correcte internationalisering vereist een grondigere aanpak, mogelijk met behulp van externe bibliotheken of API's die speciaal zijn ontworpen voor valutaformattering en -validatie in verschillende locales.
Conclusie
Runtime validatie is een essentieel onderdeel van het bouwen van robuuste en betrouwbare TypeScript-applicaties. Door TypeScript's template literal types te combineren met reguliere expressies of alternatieve validatiemethoden, kunt u een krachtige engine creƫren voor het verifiƫren van de geldigheid van strings tijdens runtime.
Deze aanpak verbetert de typeveiligheid, voorkomt onverwachte fouten en verbetert de algehele kwaliteit van uw code. Naarmate u complexere applicaties bouwt, overweeg dan om runtime validatie op te nemen om ervoor te zorgen dat uw gegevens voldoen aan de verwachte types en formaten.
Verdere Verkenning
- Verken geavanceerde technieken voor reguliere expressies voor complexere validatiescenario's.
- Onderzoek validatiebibliotheken zoals Zod en Yup voor op schema's gebaseerde validatie.
- Overweeg het gebruik van codegeneratietechnieken om automatisch validatiefuncties te genereren op basis van TypeScript-types.
- Bestudeer internationaliseringsbibliotheken en API's om locale-gevoelige gegevens te verwerken.