Täielik juhend TypeScripti 'infer'-märksõnale, selgitades selle kasutamist tingimuslike tüüpidena võimsaks tüübi väljavõtteks ja manipuleerimiseks, sealhulgas täpsemad kasutusjuhud.
TypeScripti infer-lause omandamine: Tingimuslikud tüübi väljavõtted täpsemaks tüübi manipuleerimiseks
TypeScripti tüübisüsteem on uskumatult võimas, võimaldades arendajatel luua robustseid ja hooldatavaid rakendusi. Üks peamisi funktsioone, mis seda võimu võimaldab, on infer
-märksõna, mida kasutatakse koos tingimuslike tüüpidena. See kombinatsioon pakub mehhanismi konkreetsete tüüpide väljavõtmiseks keerulistest tüübistruktuuridest. See ajaveebipostitus sukeldub sügavale infer
-märksõnasse, selgitades selle funktsionaalsust ja näidates täpsemaid kasutusjuhte. Uurime praktilisi näiteid, mida saab rakendada erinevates tarkvaraarenduse stsenaariumides, alates API interaktsioonist kuni keerulise andmestruktuuri manipuleerimiseni.
Mis on tingimuslikud tüübid?
Enne kui sukeldume infer
-i, vaatame kiiresti üle tingimuslikud tüübid. Tingimuslikud tüübid TypeScriptis võimaldavad teil määratleda tüübi tingimuse põhjal, sarnaselt JavaScripti kolmikoperaatoriga. Põhisüntaks on:
T extends U ? X : Y
See loeb: "Kui tüüp T
on tüübile U
omistatav, siis tüüp on X
; vastasel juhul on tüüp Y
."
Näide:
type IsString<T> = T extends string ? true : false;
type StringResult = IsString<string>; // type StringResult = true
type NumberResult = IsString<number>; // type NumberResult = false
Tutvustus infer
-märksõnale
infer
-märksõna kasutatakse tingimusliku tüübi extends
klauslis, et deklareerida tüübimuutuja, mida saab kontrollitavast tüübist järeldada. Sisuliselt võimaldab see teil "püüda" osa tüübist hilisemaks kasutamiseks.
Põhisüntaks:
type MyType<T> = T extends (infer U) ? U : never;
Selles näites, kui T
on omistatav mingile tüübile, püüab TypeScript järeldada U
tüüpi. Kui järeldus on edukas, on tüüp U
; vastasel juhul on see never
.
Lihtsad infer
-i näited
1. Funktsiooni tagastustüübi järeldamine
Tavaline kasutusjuht on funktsiooni tagastustüübi järeldamine:
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
function add(a: number, b: number): number {
return a + b;
}
type AddReturnType = ReturnType<typeof add>; // type AddReturnType = number
function greet(name: string): string {
return `Hello, ${name}!`;
}
type GreetReturnType = ReturnType<typeof greet>; // type GreetReturnType = string
Selles näites võtab ReturnType<T>
sisendiks funktsioonitüübi T
. See kontrollib, kas T
on omistatav funktsioonile, mis aktsepteerib mis tahes argumente ja tagastab väärtuse. Kui see nii on, järeldab see tagastustüübi kui R
ja tagastab selle. Vastasel juhul tagastab see any
.
2. Massiivi elemendi tüübi järeldamine
Teine kasulik stsenaarium on massiivi elemendi tüübi väljavõtmine:
type ArrayElementType<T> = T extends (infer U)[] ? U : never;
type NumberArrayType = ArrayElementType<number[]>; // type NumberArrayType = number
type StringArrayType = ArrayElementType<string[]>; // type StringArrayType = string
type MixedArrayType = ArrayElementType<(string | number)[]>; // type MixedArrayType = string | number
type NotAnArrayType = ArrayElementType<number>; // type NotAnArrayType = never
Siin kontrollib ArrayElementType<T>
, kas T
on massiivitüüp. Kui see on nii, järeldab see elemendi tüübi kui U
ja tagastab selle. Kui ei, siis tagastab never
.
infer
-i täpsemad kasutusjuhud
1. Konstruktori parameetrite järeldamine
Konstruktorifunktsiooni parameetritüüpide väljavõtmiseks saate kasutada infer
-i:
type ConstructorParameters<T extends new (...args: any) => any> = T extends new (...args: infer P) => any ? P : never;
class Person {
constructor(public name: string, public age: number) {}
}
type PersonConstructorParams = ConstructorParameters<typeof Person>; // type PersonConstructorParams = [string, number]
class Point {
constructor(public x: number, public y: number) {}
}
type PointConstructorParams = ConstructorParameters<typeof Point>; // type PointConstructorParams = [number, number]
Sel juhul võtab ConstructorParameters<T>
sisendiks konstruktorifunktsiooni tüübi T
. See järeldab konstruktoriparameetrite tüübid kui P
ja tagastab need tuplena.
2. Objektitüüpide omaduste väljavõtmine
infer
-i saab kasutada ka objektitüüpide konkreetsete omaduste väljavõtmiseks, kasutades kaardistatud tüüpe ja tingimuslikke tüüpe:
type PickByType<T, K extends keyof T, U> = {
[P in K as T[P] extends U ? P : never]: T[P];
};
interface User {
id: number;
name: string;
age: number;
email: string;
isActive: boolean;
}
type StringProperties = PickByType<User, keyof User, string>; // type StringProperties = { name: string; email: string; }
type NumberProperties = PickByType<User, keyof User, number>; // type NumberProperties = { id: number; age: number; }
//An interface representing geographic coordinates.
interface GeoCoordinates {
latitude: number;
longitude: number;
altitude: number;
country: string;
city: string;
timezone: string;
}
type NumberCoordinateProperties = PickByType<GeoCoordinates, keyof GeoCoordinates, number>; // type NumberCoordinateProperties = { latitude: number; longitude: number; altitude: number; }
Siin loob PickByType<T, K, U>
uue tüübi, mis sisaldab ainult T
omadusi (võtmetega K
), mille väärtused on tüübile U
omistatavad. Kaardistatud tüüp itereerib T
võtmeid ja tingimuslik tüüp filtreerib välja võtmed, mis ei vasta määratud tüübile.
3. Promise'idega töötamine
Saate Promise
-i lahendatud tüübi järeldada:
type Awaited<T> = T extends Promise<infer U> ? U : T;
async function fetchData(): Promise<string> {
return 'Data from API';
}
type FetchDataType = Awaited<ReturnType<typeof fetchData>>; // type FetchDataType = string
async function fetchNumbers(): Promise<number[]> {
return [1, 2, 3];
}
type FetchedNumbersType = Awaited<ReturnType<typeof fetchNumbers>>; //type FetchedNumbersType = number[]
Awaited<T>
tüüp võtab tüübi T
, mis eeldatavasti on Promise. Seejärel järeldab tüüp Promise'i lahendatud tüübi U
ja tagastab selle. Kui T
ei ole Promise, tagastab see T. See on TypeScripti uuemates versioonides sisseehitatud utiliittüüp.
4. Promise'ide massiivi tüübi väljavõtmine
Awaited
ja massiivitüübi järeldamise kombineerimine võimaldab teil järeldada Promise'ide massiivi lahendatud tüübi. See on eriti kasulik, kui töötate Promise.all
-iga.
type PromiseArrayReturnType<T extends Promise<any>[]> = {
[K in keyof T]: Awaited<T[K]>;
};
async function getUSDRate(): Promise<number> {
return 0.0069;
}
async function getEURRate(): Promise<number> {
return 0.0064;
}
const rates = [getUSDRate(), getEURRate()];
type RatesType = PromiseArrayReturnType<typeof rates>;
// type RatesType = [number, number]
See näide määratleb esmalt kaks asünkroonset funktsiooni, getUSDRate
ja getEURRate
, mis simuleerivad vahetuskursside hankimist. Seejärel ekstraheerib PromiseArrayReturnType
utiliittüüp lahendatud tüübi igast massiivis olevast Promise
-ist, mille tulemuseks on tupletüüp, kus iga element on vastava Promise'i oodatud tüüp.
Praktilised näited erinevates valdkondades
1. E-kaubanduse rakendus
Mõelge e-kaubanduse rakendusele, kus hangite toote üksikasju API-st. Saate tooteandmete tüübi väljavõtmiseks kasutada infer
-i:
interface Product {
id: number;
name: string;
price: number;
description: string;
imageUrl: string;
category: string;
rating: number;
countryOfOrigin: string;
}
async function fetchProduct(productId: number): Promise<Product> {
// Simulate API call
return new Promise((resolve) => {
setTimeout(() => {
resolve({
id: productId,
name: 'Example Product',
price: 29.99,
description: 'A sample product',
imageUrl: 'https://example.com/image.jpg',
category: 'Electronics',
rating: 4.5,
countryOfOrigin: 'Canada'
});
}, 500);
});
}
type ProductType = Awaited<ReturnType<typeof fetchProduct>>; // type ProductType = Product
function displayProductDetails(product: ProductType) {
console.log(`Product Name: ${product.name}`);
console.log(`Price: ${product.price} ${product.countryOfOrigin === 'Canada' ? 'CAD' : (product.countryOfOrigin === 'USA' ? 'USD' : 'EUR')}`);
}
fetchProduct(123).then(displayProductDetails);
Selles näites määratleme Product
liidese ja fetchProduct
funktsiooni, mis hangib toote üksikasjad API-st. Kasutame Awaited
ja ReturnType
, et eraldada Product
tüüp fetchProduct
funktsiooni tagastustüübist, võimaldades meil displayProductDetails
funktsiooni tüüpkontrollida.
2. Rahvusvahelistumine (i18n)
Oletagem, et teil on tõlkefunktsioon, mis tagastab sõltuvalt lokaadist erinevaid stringe. Tüübiga turvalisuse tagamiseks saate selle funktsiooni tagastustüübi väljavõtmiseks kasutada infer
-i:
interface Translations {
greeting: string;
farewell: string;
welcomeMessage: (name: string) => string;
}
const enTranslations: Translations = {
greeting: 'Hello',
farewell: 'Goodbye',
welcomeMessage: (name: string) => `Welcome, ${name}!`,
};
const frTranslations: Translations = {
greeting: 'Bonjour',
farewell: 'Au revoir',
welcomeMessage: (name: string) => `Bienvenue, ${name}!`,
};
function getTranslation(locale: 'en' | 'fr'): Translations {
return locale === 'en' ? enTranslations : frTranslations;
}
type TranslationType = ReturnType<typeof getTranslation>;
function greetUser(locale: 'en' | 'fr', name: string) {
const translations = getTranslation(locale);
console.log(translations.welcomeMessage(name));
}
greetUser('fr', 'Jean'); // Output: Bienvenue, Jean!
Siin järeldatakse TranslationType
Translations
liidesena, tagades, et greetUser
funktsioonil on tõlgitud stringidele juurdepääsemiseks õige tüübiteave.
3. API vastuse käitlemine
API-dega töötamisel võib vastuse struktuur olla keeruline. infer
saab aidata konkreetsete andmetüüpide väljavõtmisel pesastatud API vastustest:
interface ApiResponse<T> {
status: number;
data: T;
message?: string;
}
interface UserData {
id: number;
username: string;
email: string;
profile: {
firstName: string;
lastName: string;
country: string;
language: string;
}
}
async function fetchUser(userId: number): Promise<ApiResponse<UserData>> {
// Simulate API call
return new Promise((resolve) => {
setTimeout(() => {
resolve({
status: 200,
data: {
id: userId,
username: 'johndoe',
email: 'john.doe@example.com',
profile: {
firstName: 'John',
lastName: 'Doe',
country: 'USA',
language: 'en'
}
}
});
}, 500);
});
}
type UserApiResponse = Awaited<ReturnType<typeof fetchUser>>;
type UserProfileType = UserApiResponse['data']['profile'];
function displayUserProfile(profile: UserProfileType) {
console.log(`Name: ${profile.firstName} ${profile.lastName}`);
console.log(`Country: ${profile.country}`);
}
fetchUser(123).then((response) => {
if (response.status === 200) {
displayUserProfile(response.data.profile);
}
});
Selles näites määratleme ApiResponse
liidese ja UserData
liidese. Kasutame infer
-i ja tüübi indekseerimist, et eraldada UserProfileType
API vastusest, tagades, et displayUserProfile
funktsioon saab õige tüübi.
infer
-i kasutamise parimad tavad
- Hoidke see lihtsana: Kasutage
infer
-i ainult siis, kui see on vajalik. Selle üleliigne kasutamine võib muuta teie koodi raskemini loetavaks ja mõistetavaks. - Dokumenteerige oma tüübid: Lisage kommentaarid, et selgitada, mida teie tingimuslikud tüübid ja
infer
-lausendid teevad. - Testige oma tüüpe: Kasutage TypeScripti tüüpkontrolli, et tagada oma tüüpide ootuspärane käitumine.
- Kaaluge jõudlust: Keerulised tingimuslikud tüübid võivad mõnikord mõjutada kompileerimisaega. Pidage meeles oma tüüpide keerukust.
- Kasutage utiliittüüpe: TypeScript pakub mitmeid sisseehitatud utiliittüüpe (nt
ReturnType
,Awaited
), mis võivad teie koodi lihtsustada ja vähendada kohandatudinfer
-lausendite vajadust.
Levinumad probleemid
- Vale järeldus: Mõnikord võib TypeScript järeldada tüübi, mis pole see, mida te ootate. Kontrollige oma tüübi määratlusi ja tingimusi hoolikalt.
- Ringi sõltuvused: Olge ettevaatlik
infer
-i abil rekursiivsete tüüpide määratlemisel, kuna need võivad põhjustada ringi sõltuvusi ja kompileerimisvigu. - Liiga keerulised tüübid: Vältige liiga keeruliste tingimuslike tüüpide loomist, mida on raske mõista ja hooldada. Jagage need väiksemateks, paremini hallatavateks tüüpideks.
infer
-i alternatiivid
Kuigi infer
on võimas tööriist, on olukordi, kus alternatiivsed lähenemisviisid võivad olla sobivamad:
- Tüübi kinnitused: Mõnel juhul saate tüübi kinnituste abil selgesõnaliselt määrata väärtuse tüübi, selle asemel et seda järeldada. Olge aga tüübi kinnitustega ettevaatlik, kuna need võivad tüüpkontrollist mööda minna.
- Tüübiga kaitsjad: Tüübiga kaitsjaid saab kasutada väärtuse tüübi kitsendamiseks käitusajal toimuvate kontrollide põhjal. See on kasulik, kui peate käitusajal toimuvate tingimuste alusel käsitlema erinevaid tüüpe.
- Utiliittüübid: TypeScript pakub laia valikut utiliittüüpe, mis saavad hakkama paljude tavaliste tüübi manipuleerimise ülesannetega ilma kohandatud
infer
-lausendite vajaduseta.
Kokkuvõte
TypeScripti infer
-märksõna koos tingimuslike tüüpidega avab täpsemad tüübi manipuleerimise võimalused. See võimaldab teil eraldada konkreetseid tüüpe keerulistest tüübistruktuuridest, võimaldades teil kirjutada robustsemat, hooldatavamat ja tüübiga turvalisemat koodi. Alates funktsiooni tagastustüüpide järeldamisest kuni objektitüüpide omaduste väljavõtmiseni on võimalused tohutud. Mõistes selles juhendis välja toodud põhimõtteid ja parimaid tavasid, saate infer
-i kasutada täielikult ja tõsta oma TypeScripti oskusi. Pidage meeles oma tüüpide dokumenteerimist, nende põhjalikku testimist ja vajadusel kaaluge alternatiivseid lähenemisviise. infer
-i omandamine annab teile võimaluse kirjutada tõeliselt väljendusrikast ja võimsat TypeScripti koodi, mis lõppkokkuvõttes viib parema tarkvarani.