Rakenna ajonaikainen validointimoottori TypeScriptin Template Literal -tyypeille. Varmista merkkijonojen oikeellisuus ja tyyppiturvallisuus ajon aikana.
TypeScriptin Template Literal -validointimoottori: merkkijonojen ajonaikainen tarkistus
TypeScriptin malliliteraalityypit (template literal types) tarjoavat tehokkaan käännösaikaisen merkkijonojen käsittelyn ja tyyppiturvallisuuden. Nämä tarkistukset rajoittuvat kuitenkin vain käännösaikaan. Tässä blogikirjoituksessa tutkitaan, kuinka rakentaa ajonaikainen validointimoottori TypeScriptin malliliteraalityypeille, mikä mahdollistaa merkkijonojen vankan tarkistuksen ja estää mahdolliset virheet ohjelman suorituksen aikana.
Johdanto TypeScriptin Template Literal -tyyppeihin
Malliliteraalityyppien avulla voit määritellä tarkkoja merkkijonorakenteita, jotka perustuvat literaaliarvoihin, unioneihin ja tyyppipäättelyyn. Tämä mahdollistaa tarkan tyyppitarkistuksen ja automaattisen täydennyksen, mikä on erityisen hyödyllistä käsiteltäessä jäsenneltyä dataa tai toimialakohtaisia kieliä.
Tarkastellaan esimerkiksi tyyppiä valuuttakoodien esittämiseen:
type CurrencyCode = "USD" | "EUR" | "GBP";
type FormattedCurrencyString = `${CurrencyCode}-${number}`;
const validCurrency: FormattedCurrencyString = "USD-100"; // OK
const invalidCurrency: FormattedCurrencyString = "CAD-50"; // Tyyppivirhe käännösaikana
Tämä esimerkki osoittaa, kuinka TypeScript pakottaa FormattedCurrencyString-tyypin noudattamisen käännösaikana. Jos valuuttakoodi kuitenkin tulee ulkoisesta lähteestä (esim. käyttäjän syöte, API-vastaus), tarvitaan ajonaikaista validointia tyyppiturvallisuuden varmistamiseksi.
Ajonaikaisen validoinnin tarve
Vaikka TypeScript tarjoaa erinomaisen käännösaikaisen tyyppitarkistuksen, se ei voi taata ulkoisista lähteistä ajonaikaisesti vastaanotetun datan oikeellisuutta. Pelkästään käännösaikaisiin tyyppeihin luottaminen voi johtaa odottamattomiin virheisiin ja haavoittuvuuksiin.
Harkitse seuraavaa skenaariota:
function processCurrency(currencyString: FormattedCurrencyString) {
// ... logiikkaa, joka olettaa merkkijonon olevan oikein muotoiltu
}
const userInput = "CAD-50"; // Oletetaan tämän tulevan käyttäjän syötteestä
// Tämä kääntyy, mutta aiheuttaa ajonaikaisen virheen, jos logiikka
// `processCurrency`-funktion sisällä luottaa muotoon.
processCurrency(userInput as FormattedCurrencyString);
Tässä tapauksessa muunnamme (cast) userInput-muuttujan FormattedCurrencyString-tyyppiseksi, ohittaen TypeScriptin käännösaikaiset tarkistukset. Jos processCurrency-funktio olettaa merkkijonon olevan oikein muotoiltu, se kohtaa ajonaikaisen virheen.
Ajonaikainen validointi korjaa tämän puutteen varmistamalla, että ajonaikaisesti vastaanotettu data vastaa odotettuja TypeScript-tyyppejä.
Template Literal -validointimoottorin rakentaminen
Voimme rakentaa ajonaikaisen validointimoottorin käyttämällä säännöllisiä lausekkeita ja TypeScriptin tyyppijärjestelmää. Moottori ottaa syötteenä malliliteraalityypin ja merkkijonon ja palauttaa, vastaako merkkijono tyyppiä.
Vaihe 1: Tyypin määrittäminen ajonaikaista validointia varten
Ensin tarvitsemme geneerisen tyypin, joka voi edustaa malliliteraalityypin ajonaikaista vastinetta. Tämän tyypin tulisi pystyä käsittelemään erilaisia malliliteraaleja, mukaan lukien literaalit, unionit ja tyyppiparametrit.
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;
Tämä rekursiivinen tyyppimäärittely purkaa malliliteraalin osiin ja muuntaa kunkin osan säännöllisen lausekkeen malliksi.
Vaihe 2: Validointifunktion toteuttaminen
Seuraavaksi toteutamme validointifunktion, joka ottaa syötteenä malliliteraalityypin ja validoitavan merkkijonon. Tämä funktio käyttää TemplateLiteralToRegex-tyypin generoimaa säännöllistä lauseketta merkkijonon testaamiseen.
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 {
// Perusmuunnos literaalimerkkijonoille - laajenna tätä monimutkaisempia skenaarioita varten
return templateType.replace(/[.*+?^${}()|[\]]/g, '\\$&'); // Käsittele regexin erikoismerkit
}
Tämä funktio käsittelee säännöllisten lausekkeiden erikoismerkit (escape) ja luo säännöllisen lausekkeen malliliteraalityypistä, minkä jälkeen se testaa merkkijonon sitä vasten.
Vaihe 3: Validointimoottorin käyttö
Nyt voit käyttää isValid-funktiota merkkijonojen validoimiseen malliliteraalityyppejäsi vastaan ajonaikaisesti.
type CurrencyCode = "USD" | "EUR" | "GBP";
type FormattedCurrencyString = `${CurrencyCode}-${number}`;
const userInput1 = "USD-100";
const userInput2 = "CAD-50";
console.log(`'${userInput1}' on kelvollinen: ${isValid(userInput1, "USD-100" )}`); // true
console.log(`'${userInput2}' on kelvollinen: ${isValid(userInput2, "USD-100")}`); // false
console.log(`'${userInput1}' on kelvollinen: ${isValid(userInput1, `USD-${100}`)}`); // true
console.log(`'${userInput2}' on kelvollinen: ${isValid(userInput2, `USD-${100}`)}`); // false
Tämä esimerkki näyttää, kuinka isValid-funktiota käytetään käyttäjän syötteen validoimiseen FormattedCurrencyString-tyyppiä vastaan. Tuloste näyttää, pidetäänkö syötettyjä merkkijonoja kelvollisina vai ei määritetyn malliliteraalin perusteella.
Edistyneemmät validointiskenaariot
Perusvalidointimoottoria voidaan laajentaa käsittelemään monimutkaisempia skenaarioita, kuten unioneita, ehdollisia tyyppejä ja rekursiivisia tyyppejä.
Unionien käsittely
Unionien käsittelemiseksi voit muokata TemplateLiteralToRegex-tyyppiä generoimaan säännöllisen lausekkeen, joka vastaa mitä tahansa unionin jäsentä.
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 kelvollinen muotoiltu merkkijono: ${isValidUnionFormattedCurrencyString('USD-100')}`);
console.log(`'CAD-50' on kelvollinen muotoiltu merkkijono: ${isValidUnionFormattedCurrencyString('CAD-50')}`);
Ehdollisten tyyppien käsittely
Ehdollisia tyyppejä voidaan käsitellä arvioimalla ehto ajonaikaisesti ja generoimalla erilaisia säännöllisiä lausekkeita tuloksen perusteella.
type IsString = T extends string ? true : false;
// Tämä esimerkki vaatii edistyneempää logiikkaa, eikä sitä voi täysin toteuttaa yksinkertaisella säännöllisellä lausekkeella.
// Ajonaikaiset tyyppivarmennukset (type guards) tarjoavat vankemman ratkaisun tähän nimenomaiseen skenaarioon.
// Alla oleva koodi on havainnollistava ja vaatisi mukauttamista monimutkaisten ehdollisten tyyppien käsittelyyn.
function isString(value: any): value is string {
return typeof value === 'string';
}
function isValidConditionalType(value: any): boolean {
return isString(value);
}
console.log(`'hello' on merkkijono: ${isValidConditionalType('hello')}`);
console.log(`123 on merkkijono: ${isValidConditionalType(123)}`);
Rekursiivisten tyyppien käsittely
Rekursiivisia tyyppejä voidaan käsitellä määrittelemällä rekursiivinen funktio, joka generoi säännöllisen lausekkeen mallin. Ole kuitenkin varovainen välttääksesi äärettömän rekursion ja pinon ylivuotovirheet. Syvässä rekursiossa iteratiiviset lähestymistavat sopivilla rajoituksilla ovat ratkaisevan tärkeitä.
Vaihtoehtoja säännöllisille lausekkeille
Vaikka säännölliset lausekkeet ovat tehokas työkalu merkkijonojen validointiin, ne voivat olla monimutkaisia ja vaikeita ylläpitää. Muita lähestymistapoja ajonaikaiseen validointiin ovat:
- Mukautetut validointifunktiot: Kirjoita omia funktioita tiettyjen tyyppien validoimiseksi sovelluksesi vaatimusten mukaisesti.
- Tyyppivarmennukset (Type Guards): Käytä tyyppivarmennuksia muuttujan tyypin rajaamiseen ajonaikaisesti.
- Validointikirjastot: Hyödynnä olemassa olevia validointikirjastoja, kuten Zod tai Yup, validointiprosessin yksinkertaistamiseksi.
Esimerkiksi Zod tarjoaa skeemapohjaisen määrittelyn, joka muuntuu ajonaikaiseksi validoinniksi:
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("Kelvollinen valuutta:", validCurrency);
} catch (error) {
console.error("Virheellinen valuutta:", error);
}
try {
const invalidCurrency = FormattedCurrencyStringSchema.parse("CAD-50");
console.log("Kelvollinen valuutta:", invalidCurrency); //Tätä ei suoriteta, jos parse epäonnistuu.
} catch (error) {
console.error("Virheellinen valuutta:", error);
}
Parhaat käytännöt ajonaikaiseen validointiin
Kun toteutat ajonaikaista validointia, pidä mielessä seuraavat parhaat käytännöt:
- Validoi rajapinnassa: Validoi data heti sen saapuessa järjestelmään (esim. käyttäjän syötteet, API-vastaukset).
- Anna selkeät virheilmoitukset: Luo informatiivisia virheilmoituksia auttaaksesi käyttäjiä ymmärtämään, miksi heidän syötteensä on virheellinen.
- Käytä johdonmukaista validointistrategiaa: Ota käyttöön johdonmukainen validointistrategia koko sovelluksessasi datan eheyden varmistamiseksi.
- Testaa validointilogiikkasi: Testaa validointilogiikkasi perusteellisesti varmistaaksesi, että se tunnistaa oikein kelvolliset ja virheelliset tiedot.
- Tasapainota suorituskyky ja turvallisuus: Optimoi validointilogiikkasi suorituskyvyn kannalta varmistaen samalla, että se estää tehokkaasti tietoturva-aukkoja. Vältä liian monimutkaisia säännöllisiä lausekkeita, jotka voivat johtaa palvelunestohyökkäykseen.
Kansainvälistämiseen liittyviä huomioita
Käsiteltäessä merkkijonojen validointia globaalissa kontekstissa on otettava huomioon kansainvälistäminen (i18n) ja lokalisointi (l10n). Eri lokaaleilla voi olla erilaiset säännöt merkkijonojen muotoilulle, kuten päivämäärille, numeroille ja valuutta-arvoille.
Esimerkiksi euron valuuttasymboli (€) voi esiintyä summan edessä tai jälkeen lokaalista riippuen. Vastaavasti desimaalierotin voi olla piste (.) tai pilkku (,).
Näiden vaihteluiden käsittelemiseksi voit käyttää kansainvälistämiskirjastoja, kuten Intl, joka tarjoaa rajapintoja lokaalisidonnaisen datan muotoiluun ja jäsentämiseen. Voit esimerkiksi mukauttaa edellistä esimerkkiä käsittelemään erilaisia valuuttamuotoja:
function isValidCurrencyString(currencyString: string, locale: string): boolean {
try {
const formatter = new Intl.NumberFormat(locale, { style: 'currency', currency: currencyString.substring(0,3) }); //Hyvin perusesimerkki
//Yritetään jäsentää valuutta muotoilijan avulla. Tämä esimerkki on tarkoituksellisesti hyvin yksinkertainen.
return true;
} catch (error) {
return false;
}
}
console.log(`USD-100 on kelvollinen en-US:lle: ${isValidCurrencyString('USD-100', 'en-US')}`);
console.log(`EUR-100 on kelvollinen fr-FR:lle: ${isValidCurrencyString('EUR-100', 'fr-FR')}`);
Tämä koodinpätkä on perustavanlaatuinen esimerkki. Asianmukainen kansainvälistäminen vaatii perusteellisempaa käsittelyä, mahdollisesti hyödyntäen ulkoisia kirjastoja tai rajapintoja, jotka on erityisesti suunniteltu valuuttojen muotoiluun ja validointiin eri lokaaleissa.
Yhteenveto
Ajonaikainen validointi on olennainen osa vankkojen ja luotettavien TypeScript-sovellusten rakentamista. Yhdistämällä TypeScriptin malliliteraalityypit säännöllisiin lausekkeisiin tai vaihtoehtoisiin validointimenetelmiin voit luoda tehokkaan moottorin merkkijonojen oikeellisuuden tarkistamiseen ajonaikaisesti.
Tämä lähestymistapa parantaa tyyppiturvallisuutta, estää odottamattomia virheitä ja parantaa koodisi yleistä laatua. Kun rakennat monimutkaisempia sovelluksia, harkitse ajonaikaisen validoinnin sisällyttämistä varmistaaksesi, että datasi vastaa odotettuja tyyppejä ja muotoja.
Lisätutkimusta
- Tutustu edistyneisiin säännöllisten lausekkeiden tekniikoihin monimutkaisempia validointiskenaarioita varten.
- Tutki validointikirjastoja, kuten Zod ja Yup, skeemapohjaista validointia varten.
- Harkitse koodin generointitekniikoiden käyttöä validointifunktioiden automaattiseen luomiseen TypeScript-tyypeistä.
- Opiskele kansainvälistämiskirjastoja ja rajapintoja lokaalisidonnaisen datan käsittelyyn.