TypeScript / Generic функц

Generic функц

Өмнөх хичээлд generic-ийн үндсийг сурсан. Энэ хичээлд generic функцийг илүү хүчирхэг болгодог хоёр чухал онцлогийг сурна: хязгаарлалт (constraint) ба keyof оператор.

extends — төрлийн хязгаарлалт

Заримдаа T нь дурын биш, тодорхой шинж чанартай төрөл байхыг шаарддаг. extends ашиглан хязгаар тавина:

typescript
// Хязгаарлалтгүй — T.length гэж хандаж болохгүй
function уртыг_хэвлэх<T>(утга: T): void {
  console.log(утга.length); // Алдаа: T дээр length байхгүй байж болно
}

// extends ашиглан зөвхөн length property-тэй төрлийг зөвшөөрнө
function уртыг_хэвлэх<T extends { length: number }>(утга: T): number {
  return утга.length;
}

console.log(уртыг_хэвлэх("Монгол")); // 6 — string-д length байна ✅
console.log(уртыг_хэвлэх([1, 2, 3, 4])); // 4 — array-д length байна ✅
// уртыг_хэвлэх(42);  // Алдаа: number-д length байхгүй ❌

T extends { length: number } нь "T нь дурын төрөл байж болох боловч заавал length гэсэн тоон property-тэй байх ёстой" гэсэн утгатай.

Жишээ: объектын шинж чанар авах

typescript
// T extends object — T нь заавал объект байх
function шинж_авах<T extends object>(объект: T, түлхүүр: keyof T): T[keyof T] {
  return объект[түлхүүр];
}

const хэрэглэгч = { нэр: "Болд", нас: 25, хот: "Улаанбаатар" };

console.log(шинж_авах(хэрэглэгч, "нэр")); // "Болд"
console.log(шинж_авах(хэрэглэгч, "нас")); // 25
// шинж_авах(хэрэглэгч, "утас"); // Алдаа: "утас" энэ объектод байхгүй ❌

keyof — объектын түлхүүрүүдийн union

keyof нь объектын бүх property нэрийг union төрөл болгон гаргаж авдаг:

typescript
interface Хэрэглэгч {
  нэр: string;
  нас: number;
  имэйл: string;
}

type ХэрэглэгчийнТүлхүүр = keyof Хэрэглэгч;
// "нэр" | "нас" | "имэйл"

// keyof-г generic-тэй хослуулах
function аюулгүй_авах<T, K extends keyof T>(объект: T, түлхүүр: K): T[K] {
  return объект[түлхүүр];
}

const хэрэглэгч: Хэрэглэгч = { нэр: "Болд", нас: 25, имэйл: "bold@mail.com" };

const нэр: string = аюулгүй_авах(хэрэглэгч, "нэр"); // string ✅
const нас: number = аюулгүй_авах(хэрэглэгч, "нас"); // number ✅

K extends keyof T нь "K нь T-ийн аль нэг property нэр байх ёстой" гэсэн утгатай. Ингэснээр буцаах утгын төрөл T[K] болж, яг зөв төрлийг буцааж байгааг TypeScript мэддэг.

Default төрлийн параметр

Generic-д анхны утга өгч болно:

typescript
// T-д анхны утга — string
interface Жагсаалт<T = string> {
  утгууд: T[];
  нийт: number;
}

const мөрийн_жагсаалт: Жагсаалт = {
  утгууд: ["а", "б", "в"],
  нийт: 3,
};

const тооны_жагсаалт: Жагсаалт<number> = {
  утгууд: [1, 2, 3],
  нийт: 3,
};

Жагсаалт гэхэд TypeScript T = string гэж үзнэ. Тусгай төрөл хэрэгтэй бол Жагсаалт<number> гэж зааж өгнө.

Бодит жишээ: API хариуг боловсруулах функц

typescript
interface АПИХариу<T> {
  өгөгдөл: T | null;
  алдаа: string | null;
  ачааллаж: boolean;
}

async function апи_дуудах<T>(url: string): Promise<АПИХариу<T>> {
  try {
    const хариу = await fetch(url);
    const өгөгдөл: T = await хариу.json();
    return { өгөгдөл, алдаа: null, ачааллаж: false };
  } catch (алдаа) {
    return { өгөгдөл: null, алдаа: "Алдаа гарлаа", ачааллаж: false };
  }
}

// Хэрэглэгчийн мэдээлэл авах
interface Хэрэглэгч {
  id: number;
  нэр: string;
}

const хариу = await апи_дуудах<Хэрэглэгч>("/api/user/1");
// хариу.өгөгдөл нь Хэрэглэгч | null гэдгийг TypeScript мэднэ

Generic функц сурч дууссанаар API, utility, helper функцүүдийг хэдэн дахин дахин бичихгүйгээр нэг удаа зөв бичих боломжтой болно.

Дараагийн хичээлд:

Generic класс — классын property, method-д generic хэрхэн ашиглаж, дахин ашиглагдах өгөгдлийн бүтэц бүтээхийг сурна.