Uurige TypeScripti malli-literaalide tüüpe ja looge käitusaja valideerimismootor tugeva stringi kontrolli ja tüübikindluse jaoks. Õppige vältima vigu, valideerides stringe oma määratletud malli-literaalide tüüpide vastu käitusajal.
TypeScripti malli-literaalide valideerimismootor: käitusaja stringi kontroll
TypeScripti malli-literaalide tüübid pakuvad võimsat kompileerimisaja stringi manipuleerimist ja tüübikindlust. Kuid need kontrollid on piiratud kompileerimisajaga. See blogipostitus uurib, kuidas luua käitusaja valideerimismootor TypeScripti malli-literaalide tüüpide jaoks, võimaldades tugevat stringi kontrolli ja ennetades potentsiaalseid vigu programmi käivitamise ajal.
Sissejuhatus TypeScripti malli-literaalide tüüpidesse
Malli-literaalide tüübid võimaldavad teil määratleda konkreetseid stringikujusid, mis põhinevad literaalväärtustel, liitudel ja tüübi järeldamisel. See võimaldab täpset tüübi kontrolli ja automaatset lõpetamist, eriti kasulik struktureeritud andmete või domeenispetsiifiliste keeltega tegelemisel.
Näiteks kaaluge tüüpi valuutakoodide esitamiseks:
type CurrencyCode = "USD" | "EUR" | "GBP";
type FormattedCurrencyString = `${CurrencyCode}-${number}`;
const validCurrency: FormattedCurrencyString = "USD-100"; // OK
const invalidCurrency: FormattedCurrencyString = "CAD-50"; // Tüübi viga kompileerimisajal
See näide demonstreerib, kuidas TypeScript jõustab FormattedCurrencyString tüüpi kompileerimisajal. Kuid kui valuutakood pärineb välisest allikast (nt kasutaja sisend, API vastus), vajate tüübikindluse tagamiseks käitusaja valideerimist.
Vajadus käitusaja valideerimise järele
Kuigi TypeScript pakub suurepärast kompileerimisaja tüübi kontrolli, ei saa see tagada välistest allikatest käitusajal saadud andmete kehtivust. Ainult kompileerimisaja tüüpidele tuginemine võib põhjustada ootamatuid vigu ja haavatavusi.
Kaaluge järgmist stsenaariumi:
function processCurrency(currencyString: FormattedCurrencyString) {
// ... mingi loogika, mis eeldab, et string on õigesti vormindatud
}
const userInput = "CAD-50"; // Oletame, et see pärineb kasutaja sisendist
// See kompileerub, kuid põhjustab käitusaja vea, kui loogika sees
// `processCurrency` tugineb vormingule.
processCurrency(userInput as FormattedCurrencyString);
Sel juhul teisendame userInput väärtuseks FormattedCurrencyString, mööda minnes TypeScripti kompileerimisaja kontrollidest. Kui processCurrency tugineb stringi õigele vormindusele, tekib sellel käitusaja viga.
Käitusaja valideerimine ületab selle lünga, kontrollides, kas käitusajal saadud andmed vastavad eeldatavatele TypeScripti tüüpidele.
Malli-literaalide valideerimismootori ehitamine
Saame ehitada käitusaja valideerimismootori regulaaravaldiste ja TypeScripti tüübisüsteemi abil. Mootor võtab sisendina malli-literaali tüübi ja stringi ning tagastab, kas string vastab tüübile.
1. samm: tüübi määratlemine käitusaja valideerimiseks
Esiteks vajame geneerilist tüüpi, mis suudab esindada malli-literaali tüübi käitusaja ekvivalenti. See tüüp peaks suutma käsitleda erinevaid malli-literaale, sealhulgas literaale, liite ja tüübiparameetreid.
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;
See rekursiivne tüübi definitsioon jagab malli-literaali selle koostisosadeks ja teisendab iga osa regulaaravaldise mustriks.
2. samm: valideerimisfunktsiooni juurutamine
Järgmisena juurutame valideerimisfunktsiooni, mis võtab sisendina malli-literaali tüübi ja valideeritava stringi. See funktsioon kasutab stringi testimiseks TemplateLiteralToRegex genereeritud regulaaravaldist.
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 {
// Põhiline teisendus literaalstringide jaoks - laiendage seda keerukamate stsenaariumide jaoks
return templateType.replace(/[.*+?^${}()|[\]]/g, '\\$&'); // Regulaaravaldise erimärkide vältimine
}
See funktsioon väldib regulaaravaldise erimärke ja loob malli-literaali tüübist regulaaravaldise, seejärel testib stringi selle regulaaravaldise vastu.
3. samm: valideerimismootori kasutamine
Nüüd saate funktsiooni isValid kasutada stringide valideerimiseks oma malli-literaali tüüpide vastu käitusajal.
type CurrencyCode = "USD" | "EUR" | "GBP";
type FormattedCurrencyString = `${CurrencyCode}-${number}`;
const userInput1 = "USD-100";
const userInput2 = "CAD-50";
console.log(`'${userInput1}' on kehtiv: ${isValid(userInput1, "USD-100" )}`); // true
console.log(`'${userInput2}' on kehtiv: ${isValid(userInput2, "USD-100")}`); // false
console.log(`'${userInput1}' on kehtiv: ${isValid(userInput1, `USD-${100}`)}`); // true
console.log(`'${userInput2}' on kehtiv: ${isValid(userInput2, `USD-${100}`)}`); // false
See näide demonstreerib, kuidas funktsiooni isValid kasutada kasutaja sisendi valideerimiseks FormattedCurrencyString tüübi vastu. Väljund näitab, kas sisendstringid on määratud malli-literaali alusel kehtivad või mitte.
Täiustatud valideerimise stsenaariumid
Põhilist valideerimismootorit saab laiendada, et käsitleda keerukamaid stsenaariume, nagu liidud, tingimuslikud tüübid ja rekursiivsed tüübid.
Liitude käsitlemine
Liitude käsitlemiseks saate muuta tüüpi TemplateLiteralToRegex, et genereerida regulaaravaldis, mis vastab igale liidu liikmele.
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' on kehtiv vormindatud string: ${isValidUnionFormattedCurrencyString('USD-100')}`);
console.log(`'CAD-50' on kehtiv vormindatud string: ${isValidUnionFormattedCurrencyString('CAD-50')}`);
Tingimuslike tüüpide käsitlemine
Tingimuslikke tüüpe saab käsitleda, hinnates tingimust käitusajal ja genereerides tulemuse põhjal erinevaid regulaaravaldisi.
type IsString = T extends string ? true : false;
// See näide nõuab keerukamat loogikat ja seda pole võimalik täielikult juurutada lihtsa regexi abil.
// Käitusaja tüübikaitsed pakuvad selles konkreetses stsenaariumis töökindlamat lahendust.
// Allolev kood on illustreeriv ja seda oleks vaja kohandada keerukate tingimuslike tüüpide käsitlemiseks.
function isString(value: any): value is string {
return typeof value === 'string';
}
function isValidConditionalType(value: any): boolean {
return isString(value);
}
console.log(`'hello' on string: ${isValidConditionalType('hello')}`);
console.log(`123 on string: ${isValidConditionalType(123)}`);
Rekursiivsete tüüpide käsitlemine
Rekursiivseid tüüpe saab käsitleda, määratledes rekursiivse funktsiooni, mis genereerib regulaaravaldise mustri. Kuid olge ettevaatlik lõpmatu rekursiooni ja pinu ületäitumise vigade vältimiseks. Sügava rekursiooni korral on iteratiivsed lähenemisviisid koos sobivate piirangutega üliolulised.
Alternatiivid regulaaravaldistele
Kuigi regulaaravaldised on võimas tööriist stringi valideerimiseks, võivad need olla keerulised ja raskesti hooldatavad. Muud käitusaja valideerimise lähenemisviisid hõlmavad järgmist:
- Kohandatud valideerimisfunktsioonid: kirjutage kohandatud funktsioone konkreetsete tüüpide valideerimiseks vastavalt oma rakenduse nõuetele.
- Tüübi kaitsed: kasutage tüübikaitsmeid, et kitsendada muutuja tüüpi käitusajal.
- Valideerimisteegid: kasutage olemasolevaid valideerimisteeke, nagu Zod või Yup, et valideerimisprotsessi lihtsustada.
Näiteks Zod pakub skeemipõhist deklaratsiooni, mis tõlgendatakse valideerimise käitusajaks:
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("Kehtiv valuuta:", validCurrency);
} catch (error) {
console.error("Kehtetu valuuta:", error);
}
try {
const invalidCurrency = FormattedCurrencyStringSchema.parse("CAD-50");
console.log("Kehtiv valuuta:", invalidCurrency); //See ei käivitu, kui parse ebaõnnestub.
} catch (error) {
console.error("Kehtetu valuuta:", error);
}
Parimad praktikad käitusaja valideerimiseks
Käitusaja valideerimise juurutamisel pidage meeles järgmisi parimaid praktikaid:
- Valideerige piiril: valideerige andmed kohe, kui need teie süsteemi sisenevad (nt kasutaja sisend, API vastused).
- Esitage selged veateated: genereerige informatiivseid veateateid, et aidata kasutajatel mõista, miks nende sisend on kehtetu.
- Kasutage järjepidevat valideerimisstrateegiat: võtke andmete terviklikkuse tagamiseks kasutusele järjepidev valideerimisstrateegia kogu oma rakenduses.
- Testige oma valideerimisloogikat: testige oma valideerimisloogikat põhjalikult, et tagada selle õige tuvastamine kehtivate ja kehtetute andmete korral.
- Tasakaalustage jõudlust ja turvalisust: optimeerige oma valideerimisloogikat jõudluse jaoks, tagades samal ajal, et see hoiab tõhusalt ära turvaauke. Vältige liiga keerulist regexi, mis viib teenuse keelamiseni.
Rahvusvahelistamise kaalutlused
Stringi valideerimisel globaalses kontekstis peate arvestama rahvusvahelistamise (i18n) ja lokaliseerimisega (l10n). Erinevatel lokaatidel võivad olla erinevad reeglid stringide vormindamiseks, näiteks kuupäevad, numbrid ja valuutaväärtused.
Näiteks võib euro (€) sümbol ilmuda enne või pärast summat, olenevalt lokaadist. Samamoodi võib kümnenderaldajaks olla punkt (.) või koma (,).
Nende variatsioonide käsitlemiseks saate kasutada rahvusvahelistamise teeke, nagu Intl, mis pakub API-sid lokaaditundlike andmete vormindamiseks ja parsimiseks. Näiteks saate eelmise näite kohandada erinevate valuutavormingute käsitlemiseks:
function isValidCurrencyString(currencyString: string, locale: string): boolean {
try {
const formatter = new Intl.NumberFormat(locale, { style: 'currency', currency: currencyString.substring(0,3) }); //Väga lihtne näide
//Proovige valuutat parserdada, kasutades vormindajat. See näide on tahtlikult väga lihtne.
return true;
} catch (error) {
return false;
}
}
console.log(`USD-100 on kehtiv en-US jaoks: ${isValidCurrencyString('USD-100', 'en-US')}`);
console.log(`EUR-100 on kehtiv fr-FR jaoks: ${isValidCurrencyString('EUR-100', 'fr-FR')}`);
See koodijupp annab põhjaliku näite. Nõuetekohane rahvusvahelistamine nõuab põhjalikumat käsitlemist, potentsiaalselt kasutades väliseid teeke või API-sid, mis on spetsiaalselt loodud valuuta vormindamiseks ja valideerimiseks erinevates lokaatides.
Järeldus
Käitusaja valideerimine on oluline osa tugevate ja usaldusväärsete TypeScripti rakenduste ehitamisest. Kombineerides TypeScripti malli-literaalide tüübid regulaaravaldiste või alternatiivsete valideerimismeetoditega, saate luua võimsa mootori stringide kehtivuse kontrollimiseks käitusajal.
See lähenemisviis suurendab tüübikindlust, hoiab ära ootamatuid vigu ja parandab teie koodi üldist kvaliteeti. Kui ehitate keerukamaid rakendusi, kaaluge käitusaja valideerimise kaasamist, et tagada teie andmete vastavus eeldatavatele tüüpidele ja vormingutele.
Edasine uurimine
- Uurige täiustatud regulaaravaldise tehnikaid keerukamate valideerimisstsenaariumide jaoks.
- Uurige valideerimisteeke, nagu Zod ja Yup, skeemipõhise valideerimise jaoks.
- Kaaluge koodi genereerimise tehnikaid valideerimisfunktsioonide automaatseks genereerimiseks TypeScripti tüüpidest.
- Uurige rahvusvahelistamise teeke ja API-sid lokaaditundlike andmete käsitlemiseks.