Tyrinėkite TypeScript šabloninius literalius ir sąlyginius tipus, kad rašytumėte išraiškingesnį bei patikimesnį kodą. Įvaldykite tipų manipuliacijas sudėtingiems scenarijams.
TypeScript išplėstiniai tipai: šabloninių literalių ir sąlyginių tipų įsisavinimas
TypeScript stiprybė slypi galingoje tipų sistemoje. Nors pagrindiniai tipai, tokie kaip string, number ir boolean, yra pakankami daugeliui scenarijų, išplėstinės funkcijos, pavyzdžiui, šabloniniai literalų tipai ir sąlyginiai tipai, atveria naują išraiškingumo ir tipų saugumo lygį. Šis vadovas pateikia išsamią šių išplėstinių tipų apžvalgą, nagrinėjant jų galimybes ir demonstruojant praktinius pritaikymus.
Šabloninių literalių tipų supratimas
Šabloniniai literalų tipai remiasi JavaScript šabloniniais literalais, leisdami apibrėžti tipus pagal eilutės interpoliaciją. Tai leidžia kurti tipus, kurie atspindi konkrečius eilutės šablonus, todėl jūsų kodas tampa tvirtesnis ir labiau nuspėjamas.
Pagrindinė sintaksė ir naudojimas
Šabloniniai literalų tipai naudoja atvirkštinius apostrofus (`) tipui apibrėžti, panašiai kaip JavaScript šabloniniai literalai. Atvirkštiniuose apostrofuose galite interpoliuoti kitus tipus naudodami ${} sintaksę. Čia ir slypi magija – jūs iš esmės sukuriate tipą, kuris yra eilutė, sukonstruota kompiliavimo metu pagal tipus, esančius interpoliacijoje.
type HTTPMethod = "GET" | "POST" | "PUT" | "DELETE";
type APIEndpoint = `/api/${string}`;
// Example Usage
const getEndpoint: APIEndpoint = "/api/users"; // Valid
const postEndpoint: APIEndpoint = "/api/products/123"; // Valid
const invalidEndpoint: APIEndpoint = "/admin/settings"; // TypeScript will not show an error here as `string` can be anything
Šiame pavyzdyje APIEndpoint yra tipas, atspindintis bet kokią eilutę, prasidedančią /api/. Nors šis paprastas pavyzdys yra naudingas, tikroji šabloninių literalų tipų galia atsiskleidžia juos derinant su konkretesniais tipų apribojimais.
Derinimas su sąjungos tipais
Šabloniniai literalų tipai ypač atsiskleidžia, kai naudojami su sąjungos tipais. Tai leidžia jums sukurti tipus, kurie atspindi konkretų eilutės derinių rinkinį.
type HTTPMethod = "GET" | "POST" | "PUT" | "DELETE";
type APIPath = "users" | "products" | "orders";
type APIEndpoint = `/${APIPath}/${HTTPMethod}`;
// Valid API Endpoints
const getUsers: APIEndpoint = "/users/GET";
const postProducts: APIEndpoint = "/products/POST";
// Invalid API Endpoints (will result in TypeScript errors)
// const invalidEndpoint: APIEndpoint = "/users/PATCH"; // Error: "/users/PATCH" is not assignable to type "/users/GET" | "/users/POST" | "/users/PUT" | "/users/DELETE" | "/products/GET" | "/products/POST" | ... 3 more ... | "/orders/DELETE".
Dabar APIEndpoint yra labiau ribojantis tipas, leidžiantis tik konkrečius API kelių ir HTTP metodų derinius. TypeScript pažymės bet kokius bandymus naudoti neteisingus derinius, taip padidindamas tipų saugumą.
Eilučių manipuliavimas su šabloniniais literalų tipais
TypeScript pateikia vidinius eilučių manipuliavimo tipus, kurie sklandžiai veikia su šabloniniais literalų tipais. Šie tipai leidžia transformuoti eilutes kompiliavimo metu.
- Uppercase: Pakeičia eilutę į didžiąsias raides.
- Lowercase: Pakeičia eilutę į mažąsias raides.
- Capitalize: Pakeičia pirmąją eilutės raidę į didžiąją.
- Uncapitalize: Pakeičia pirmąją eilutės raidę į mažąją.
type Greeting = "hello world";
type UppercaseGreeting = Uppercase; // "HELLO WORLD"
type LowercaseGreeting = Lowercase; // "hello world"
type CapitalizedGreeting = Capitalize; // "Hello world"
type UncapitalizedGreeting = Uncapitalize; // "hello world"
Šie eilučių manipuliavimo tipai yra ypač naudingi automatiškai generuojant tipus pagal pavadinimų suteikimo taisykles. Pavyzdžiui, galite išvesti veiksmų tipus iš įvykių pavadinimų arba atvirkščiai.
Praktinis šabloninių literalų tipų pritaikymas
- API galinių taškų apibrėžimas: Kaip parodyta aukščiau, apibrėžiant API galinius taškus su tiksliais tipų apribojimais.
- Įvykių apdorojimas: Kuriant tipus įvykių pavadinimams su specifiniais priešdėliais ir priesagomis.
- CSS klasių generavimas: Generuojant CSS klasių pavadinimus pagal komponentų pavadinimus ir būsenas.
- Duomenų bazės užklausų kūrimas: Užtikrinant tipų saugumą konstruojant duomenų bazės užklausas.
Tarptautinis pavyzdys: valiutos formatavimas
Įsivaizduokite, kad kuriate finansinę programą, palaikančią kelias valiutas. Galite naudoti šabloninius literalų tipus, kad užtikrintumėte teisingą valiutos formatavimą.
type CurrencyCode = "USD" | "EUR" | "GBP" | "JPY";
type CurrencyFormat = `${number} ${T}`;
const priceUSD: CurrencyFormat<"USD"> = "100 USD"; // Valid
const priceEUR: CurrencyFormat<"EUR"> = "50 EUR"; // Valid
// const priceInvalid: CurrencyFormat<"USD"> = "100 EUR"; // Error: Type 'string' is not assignable to type '`${number} USD`'.
function formatCurrency(amount: number, currency: T): CurrencyFormat {
return `${amount} ${currency}`;
}
const formattedUSD = formatCurrency(250, "USD"); // Type: "250 USD"
const formattedEUR = formatCurrency(100, "EUR"); // Type: "100 EUR"
Šis pavyzdys užtikrina, kad valiutų vertės visada būtų formatuojamos su teisingu valiutos kodu, taip išvengiant galimų klaidų.
Pasineriant į sąlyginius tipus
Sąlyginiai tipai įveda šakojimosi logiką į TypeScript tipų sistemą, leisdami jums apibrėžti tipus, kurie priklauso nuo kitų tipų. Ši funkcija yra nepaprastai galinga kuriant labai lanksčius ir pakartotinai naudojamus tipų apibrėžimus.
Pagrindinė sintaksė ir naudojimas
Sąlyginiai tipai naudoja infer raktažodį ir trejybės operatorių (sąlyga ? teisingasTipas : klaidingasTipas), kad apibrėžtų tipų sąlygas.
type IsString = T extends string ? true : false;
type StringCheck = IsString; // type StringCheck = true
type NumberCheck = IsString; // type NumberCheck = false
Šiame pavyzdyje IsString yra sąlyginis tipas, kuris tikrina, ar T yra priskiriamas string tipui. Jei taip, tipas išsisprendžia į true; kitu atveju – į false.
infer raktažodis
infer raktažodis leidžia išgauti tipą iš kito tipo. Tai ypač naudinga dirbant su sudėtingais tipais, pavyzdžiui, funkcijų ar masyvų tipais.
type ReturnType any> = T extends (...args: any) => infer R ? R : any;
function add(a: number, b: number): number {
return a + b;
}
type AddReturnType = ReturnType; // type AddReturnType = number
Šiame pavyzdyje ReturnType išgauna funkcijos tipo T grąžinamąjį tipą. infer R dalis sąlyginiame tipe išveda grąžinamąjį tipą ir priskiria jį tipo kintamajam R. Jei T nėra funkcijos tipas, tipas išsisprendžia į any.
Paskirstomieji sąlyginiai tipai
Sąlyginiai tipai tampa paskirstomieji, kai tikrinamas tipas yra „nuogas“ tipo parametras. Tai reiškia, kad sąlyginis tipas yra taikomas kiekvienam sąjungos tipo nariui atskirai.
type ToArray = T extends any ? T[] : never;
type NumberOrStringArray = ToArray; // type NumberOrStringArray = string[] | number[]
Šiame pavyzdyje ToArray konvertuoja tipą T į masyvo tipą. Kadangi T yra „nuogas“ tipo parametras (neįvyniotas į kitą tipą), sąlyginis tipas taikomas number ir string atskirai, todėl gaunama number[] ir string[] sąjunga.
Praktinis sąlyginių tipų pritaikymas
- Grąžinamųjų tipų išgavimas: Kaip parodyta aukščiau, išgaunant funkcijos grąžinamąjį tipą.
- Tipų filtravimas iš sąjungos: Kuriant tipą, kuris apima tik tam tikrus tipus iš sąjungos.
- Perkrautų funkcijų tipų apibrėžimas: Kuriant skirtingus funkcijų tipus pagal įvesties tipus.
- Tipų apsaugų (Type Guards) kūrimas: Apibrėžiant funkcijas, kurios susiaurina kintamojo tipą.
Tarptautinis pavyzdys: skirtingų datos formatų apdorojimas
Skirtinguose pasaulio regionuose naudojami skirtingi datos formatai. Galite naudoti sąlyginius tipus, kad apdorotumėte šiuos skirtumus.
type DateFormat = "YYYY-MM-DD" | "MM/DD/YYYY" | "DD.MM.YYYY";
type ParseDate = T extends "YYYY-MM-DD"
? { year: number; month: number; day: number; format: "YYYY-MM-DD" }
: T extends "MM/DD/YYYY"
? { month: number; day: number; year: number; format: "MM/DD/YYYY" }
: T extends "DD.MM.YYYY"
? { day: number; month: number; year: number; format: "DD.MM.YYYY" }
: never;
function parseDate(dateString: string, format: T): ParseDate {
// (Implementation would handle different date formats)
if (format === "YYYY-MM-DD") {
const [year, month, day] = dateString.split("-").map(Number);
return { year, month, day, format } as ParseDate;
} else if (format === "MM/DD/YYYY") {
const [month, day, year] = dateString.split("/").map(Number);
return { month, day, year, format } as ParseDate;
} else if (format === "DD.MM.YYYY") {
const [day, month, year] = dateString.split(".").map(Number);
return { day, month, year, format } as ParseDate;
} else {
throw new Error("Invalid date format");
}
}
const parsedDateISO = parseDate("2023-10-27", "YYYY-MM-DD"); // Type: { year: number; month: number; day: number; format: "YYYY-MM-DD"; }
const parsedDateUS = parseDate("10/27/2023", "MM/DD/YYYY"); // Type: { month: number; day: number; year: number; format: "MM/DD/YYYY"; }
const parsedDateEU = parseDate("27.10.2023", "DD.MM.YYYY"); // Type: { day: number; month: number; year: number; format: "DD.MM.YYYY"; }
console.log(parsedDateISO.year); // Access the year knowing it will be there
Šis pavyzdys naudoja sąlyginius tipus, kad apibrėžtų skirtingas datos analizavimo funkcijas pagal nurodytą datos formatą. ParseDate tipas užtikrina, kad grąžinamas objektas turėtų teisingas savybes, atsižvelgiant į formatą.
Šabloninių literalių ir sąlyginių tipų derinimas
Tikroji galia atsiskleidžia, kai derinate šabloninius literalų tipus ir sąlyginius tipus. Tai leidžia atlikti neįtikėtinai galingas tipų manipuliacijas.
type EventName = `on${Capitalize}`;
type ExtractEventPayload = T extends EventName
? { type: T; payload: any } // Simplified for demonstration
: never;
type ClickEvent = EventName<"click">; // "onClick"
type MouseOverEvent = EventName<"mouseOver">; // "onMouseOver"
//Example function that takes a type
function processEvent(event: T): ExtractEventPayload {
//In a real implementation, we would actually dispatch the event.
console.log(`Processing event ${event}`);
//In a real implementation, the payload would be based on event type.
return { type: event, payload: {} } as ExtractEventPayload;
}
//Note that the return types are very specific:
const clickEvent = processEvent("onClick"); // { type: "onClick"; payload: any; }
const mouseOverEvent = processEvent("onMouseOver"); // { type: "onMouseOver"; payload: any; }
//If you use other strings, you get never:
// const someOtherEvent = processEvent("someOtherEvent"); // Type is `never`
Geroji praktika ir aspektai
- Paprastumas: Nors ir galingi, šie išplėstiniai tipai gali greitai tapti sudėtingi. Siekite aiškumo ir lengvos priežiūros.
- Kruopštus testavimas: Užtikrinkite, kad jūsų tipų apibrėžimai veiktų kaip tikėtasi, rašydami išsamius vienetų testus (unit tests).
- Dokumentuokite savo kodą: Aiškiai dokumentuokite savo išplėstinių tipų paskirtį ir veikimą, kad pagerintumėte kodo skaitomumą.
- Atsižvelkite į našumą: Pernelyg gausus išplėstinių tipų naudojimas gali paveikti kompiliavimo laiką. Profiluokite savo kodą ir optimizuokite, kur reikia.
Išvados
Šabloniniai literalų tipai ir sąlyginiai tipai yra galingi įrankiai TypeScript arsenale. Įvaldę šiuos išplėstinius tipus, galite rašyti išraiškingesnį, lengviau prižiūrimą ir tipų atžvilgiu saugesnį kodą. Šios funkcijos leidžia jums užfiksuoti sudėtingus ryšius tarp tipų, taikyti griežtesnius apribojimus ir kurti labai pakartotinai naudojamus tipų apibrėžimus. Pritaikykite šias technikas, kad patobulintumėte savo TypeScript įgūdžius ir kurtumėte tvirtas bei plečiamas programas pasaulinei auditorijai.