TypeScript'ning 'infer' kalit so'zi bo'yicha to'liq qo'llanma. Kuchli tip ajratish va manipulyatsiya qilish uchun uni shartli tiplar bilan qo'llash tushuntirilgan.
TypeScript Infer'ni chuqur o'rganish: Murakkab tip manipulyatsiyalari uchun shartli tip ajratish
TypeScript'ning tiplar tizimi nihoyatda kuchli bo'lib, dasturchilarga mustahkam va qo'llab-quvvatlash oson bo'lgan ilovalar yaratish imkonini beradi. Bu kuchni ta'minlovchi asosiy xususiyatlardan biri shartli tiplar bilan birgalikda ishlatiladigan infer
kalit so'zidir. Bu kombinatsiya murakkab tip tuzilmalaridan ma'lum tiplarni ajratib olish mexanizmini taqdim etadi. Ushbu blog postida infer
kalit so'zi chuqur tahlil qilinadi, uning funksionalligi tushuntiriladi va ilg'or qo'llash holatlari namoyish etiladi. Biz API bilan ishlashdan tortib murakkab ma'lumotlar tuzilmalarini manipulyatsiya qilishgacha bo'lgan turli dasturiy ta'minotni ishlab chiqish stsenariylariga qo'llaniladigan amaliy misollarni ko'rib chiqamiz.
Shartli Tiplar nima?
infer
'ga sho'ng'ishdan oldin, keling, shartli tiplarni tezda ko'rib chiqaylik. TypeScript'dagi shartli tiplar JavaScript'dagi ternar operatoriga o'xshab, shartga asoslangan tipni aniqlashga imkon beradi. Asosiy sintaksisi:
T extends U ? X : Y
Bu shunday o'qiladi: "Agar T
tipi U
tipiga tayinlana olsa, u holda tip X
bo'ladi; aks holda, tip Y
bo'ladi."
Misol:
type IsString<T> = T extends string ? true : false;
type StringResult = IsString<string>; // type StringResult = true
type NumberResult = IsString<number>; // type NumberResult = false
infer
Kalit So'zi bilan Tanishtiruv
infer
kalit so'zi shartli tipning extends
bandi ichida tekshirilayotgan tipdan chiqarilishi mumkin bo'lgan tip o'zgaruvchisini e'lon qilish uchun ishlatiladi. Mohiyatan, u sizga tipning bir qismini keyinchalik foydalanish uchun "ushlab olish" imkonini beradi.
Asosiy Sintaksis:
type MyType<T> = T extends (infer U) ? U : never;
Ushbu misolda, agar T
biror tipga tayinlana olsa, TypeScript U
tipini aniqlashga harakat qiladi. Agar aniqlash muvaffaqiyatli bo'lsa, tip U
bo'ladi; aks holda, u never
bo'ladi.
infer
'ning Oddiy Misollari
1. Funksiyaning Qaytariladigan Tipini Aniqlash
Keng tarqalgan qo'llash holatlaridan biri bu funksiyaning qaytariladigan tipini aniqlashdir:
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
Ushbu misolda, ReturnType<T>
kirish sifatida T
funksiya tipini qabul qiladi. U T
ning har qanday argumentlarni qabul qiluvchi va qiymat qaytaruvchi funksiyaga tayinlanishini tekshiradi. Agar shunday bo'lsa, u qaytariladigan tipni R
sifatida aniqlaydi va uni qaytaradi. Aks holda, u any
ni qaytaradi.
2. Massiv Elementi Tipini Aniqlash
Yana bir foydali holat - bu massivdan element tipini ajratib olish:
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
Bu yerda, ArrayElementType<T>
T
ning massiv tipi ekanligini tekshiradi. Agar shunday bo'lsa, u element tipini U
sifatida aniqlaydi va uni qaytaradi. Agar yo'q bo'lsa, u never
ni qaytaradi.
infer
'ning Murakkab Qo'llash Holatlari
1. Konstruktor Parametrlarini Aniqlash
Siz infer
yordamida konstruktor funksiyasining parametr tiplarini ajratib olishingiz mumkin:
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]
Bu holda, ConstructorParameters<T>
T
konstruktor funksiya tipini qabul qiladi. U konstruktor parametrlarining tiplarini P
sifatida aniqlaydi va ularni kortej (tuple) sifatida qaytaradi.
2. Obyekt Tiplaridan Xususiyatlarni Ajratib Olish
infer
shuningdek, map qilingan tiplar va shartli tiplar yordamida obyekt tiplaridan ma'lum xususiyatlarni ajratib olish uchun ishlatilishi mumkin:
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; }
//Geografik koordinatalarni ifodalovchi interfeys.
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; }
Bu yerda, PickByType<T, K, U>
faqatgina T
ning qiymatlari U
tipiga mos keladigan xususiyatlarini (K
dagi kalitlar bilan) o'z ichiga olgan yangi tip yaratadi. Map qilingan tip T
ning kalitlari bo'ylab iteratsiya qiladi va shartli tip belgilangan tipga mos kelmaydigan kalitlarni filtrlaydi.
3. Promise'lar bilan Ishlash
Siz Promise
'ning yakunlangan (resolved) tipini aniqlashingiz mumkin:
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>
tipi Promise bo'lishi kutilgan T
tipini qabul qiladi. Keyin bu tip Promise'ning yakunlangan tipi U
ni aniqlaydi va uni qaytaradi. Agar T
promise bo'lmasa, u T'ni qaytaradi. Bu TypeScript'ning yangi versiyalarida o'rnatilgan yordamchi tipdir.
4. Promise'lar Massivi Tipini Ajratib Olish
Awaited
va massiv tipini aniqlashni birlashtirish sizga Promise'lar massivi tomonidan yakunlanadigan tipni aniqlash imkonini beradi. Bu ayniqsa Promise.all
bilan ishlashda foydalidir.
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]
Ushbu misolda birinchi bo'lib valyuta kurslarini olishni simulyatsiya qiluvchi ikkita asinxron funksiya, getUSDRate
va getEURRate
, aniqlanadi. Keyin PromiseArrayReturnType
yordamchi tipi massivdagi har bir Promise
'dan yakunlangan tipni ajratib oladi, natijada har bir elementi tegishli Promise'ning kutilgan tipi bo'lgan kortej tipi hosil bo'ladi.
Turli Sohalardagi Amaliy Misollar
1. Elektron Tijorat Ilovasi
API'dan mahsulot tafsilotlarini oladigan elektron tijorat ilovasini ko'rib chiqing. Siz infer
yordamida mahsulot ma'lumotlari tipini ajratib olishingiz mumkin:
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);
Ushbu misolda biz Product
interfeysini va API'dan mahsulot tafsilotlarini oladigan fetchProduct
funksiyasini aniqlaymiz. Biz Awaited
va ReturnType
'dan foydalanib, fetchProduct
funksiyasining qaytarish tipidan Product
tipini ajratib olamiz, bu esa displayProductDetails
funksiyasini tip bo'yicha tekshirish imkonini beradi.
2. Internatsionalizatsiya (i18n)
Aytaylik, sizda lokatsiyaga qarab turli satrlarni qaytaradigan tarjima funksiyasi bor. Siz infer
yordamida tip xavfsizligi uchun ushbu funksiyaning qaytariladigan tipini ajratib olishingiz mumkin:
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!
Bu yerda, TranslationType
Translations
interfeysi sifatida aniqlanadi, bu esa greetUser
funksiyasining tarjima qilingan satrlarga kirish uchun to'g'ri tip ma'lumotiga ega bo'lishini ta'minlaydi.
3. API Javoblarini Ishlash
API'lar bilan ishlaganda javob strukturasi murakkab bo'lishi mumkin. infer
ichma-ich joylashgan API javoblaridan ma'lum ma'lumotlar turlarini ajratib olishga yordam beradi:
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);
}
});
Ushbu misolda biz ApiResponse
interfeysi va UserData
interfeysini aniqlaymiz. Biz infer
va tip indeksatsiyasidan foydalanib, API javobidan UserProfileType
ni ajratib olamiz, bu esa displayUserProfile
funksiyasining to'g'ri tipni qabul qilishini ta'minlaydi.
infer
'dan Foydalanish bo'yicha Eng Yaxshi Amaliyotlar
- Sodda tuting:
infer
'dan faqat zarur bo'lganda foydalaning. Uni haddan tashqari ko'p ishlatish kodingizni o'qish va tushunishni qiyinlashtirishi mumkin. - Tiplaringizni Hujjatlashtiring: Shartli tiplaringiz va
infer
iboralaringiz nima qilayotganini tushuntirish uchun izohlar qo'shing. - Tiplaringizni Sinab Ko'ring: Tiplaringiz kutilganidek ishlayotganiga ishonch hosil qilish uchun TypeScript'ning tip tekshiruvidan foydalaning.
- Ishlash Samaradorligini Hisobga Oling: Murakkab shartli tiplar ba'zan kompilyatsiya vaqtiga ta'sir qilishi mumkin. Tiplaringizning murakkabligiga e'tiborli bo'ling.
- Yordamchi Tiplardan Foydalaning: TypeScript kodingizni soddalashtiradigan va maxsus
infer
iboralariga ehtiyojni kamaytiradigan bir nechta o'rnatilgan yordamchi tiplarni (masalan,ReturnType
,Awaited
) taqdim etadi.
Keng Tarqalgan Xatolar
- Noto'g'ri Aniqlash: Ba'zan TypeScript siz kutmagan tipni aniqlashi mumkin. Tip ta'riflaringiz va shartlaringizni ikki marta tekshiring.
- Siklik Bog'liqliklar:
infer
yordamida rekursiv tiplarni aniqlashda ehtiyot bo'ling, chunki ular siklik bog'liqliklarga va kompilyatsiya xatolariga olib kelishi mumkin. - Haddan Tashqari Murakkab Tiplar: Tushunish va qo'llab-quvvatlash qiyin bo'lgan haddan tashqari murakkab shartli tiplarni yaratishdan saqlaning. Ularni kichikroq, boshqarilishi osonroq tiplarga ajrating.
infer
'ga Alternativalar
infer
kuchli vosita bo'lsa-da, ba'zi hollarda alternativ yondashuvlar yanada mosroq bo'lishi mumkin:
- Tiplarni Tasdiqlash (Type Assertions): Ba'zi hollarda, qiymatning tipini aniqlash o'rniga uni aniq belgilash uchun tiplarni tasdiqlashdan foydalanishingiz mumkin. Biroq, tiplarni tasdiqlash bilan ehtiyot bo'ling, chunki ular tip tekshiruvini chetlab o'tishi mumkin.
- Tiplarni Himoyalash (Type Guards): Tiplarni himoyalashdan ish vaqtidagi tekshiruvlar asosida qiymat tipini toraytirish uchun foydalanish mumkin. Bu ish vaqtidagi shartlarga qarab turli tiplarni boshqarish kerak bo'lganda foydalidir.
- Yordamchi Tiplar: TypeScript ko'plab umumiy tip manipulyatsiyasi vazifalarini maxsus
infer
iboralariga ehtiyoj sezmasdan bajaradigan boy yordamchi tiplar to'plamini taqdim etadi.
Xulosa
TypeScript'dagi infer
kalit so'zi shartli tiplar bilan birgalikda qo'llanilganda, ilg'or tip manipulyatsiyasi imkoniyatlarini ochib beradi. U sizga murakkab tip tuzilmalaridan ma'lum tiplarni ajratib olish imkonini beradi, bu esa yanada mustahkam, qo'llab-quvvatlanadigan va tip jihatidan xavfsiz kod yozishga yordam beradi. Funksiya qaytaradigan tiplarni aniqlashdan tortib, obyekt tiplaridan xususiyatlarni ajratib olishgacha, imkoniyatlar juda keng. Ushbu qo'llanmada keltirilgan tamoyillar va eng yaxshi amaliyotlarni tushunib, siz infer
'dan to'liq salohiyatida foydalanishingiz va TypeScript mahoratingizni oshirishingiz mumkin. Tiplaringizni hujjatlashtirishni, ularni sinchkovlik bilan sinab ko'rishni va kerak bo'lganda alternativ yondashuvlarni ko'rib chiqishni unutmang. infer
'ni o'zlashtirish sizga haqiqatan ham ifodali va kuchli TypeScript kodi yozish imkoniyatini beradi, bu esa oxir-oqibat yaxshiroq dasturiy ta'minotga olib keladi.