O'zbek

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

Keng Tarqalgan Xatolar

infer'ga Alternativalar

infer kuchli vosita bo'lsa-da, ba'zi hollarda alternativ yondashuvlar yanada mosroq bo'lishi mumkin:

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.