TypeScript / Reflect Metadata

Reflect Metadata

Өмнөх хичээлд decorator-ийн үндсийг сурсан. Одоо decorator-ийг бүр хүчирхэг болгодог хэрэгсэл — Reflect Metadata API-г сурна. Энэ API нь class болон method-ийн тухай мэдээллийг (metadata) хадгалж, дараа нь уншиж авах боломжийг олгодог.

NestJS, TypeORM, Angular зэрэг томоохон framework-үүд яг энэ API-г ашиглан dependency injection болон ORM-ийн суурийг бүтээдэг.

Reflect Metadata суулгах

Reflect Metadata нь Node.js-д суурилагдаагүй тул тусдаа package суулгах хэрэгтэй:

bash
npm install reflect-metadata

Дараа нь tsconfig.json-д тохиргоо хийнэ:

json
{
  "compilerOptions": {
    "target": "ES2020",
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}

Програмынхаа хамгийн эхний файлд (main.ts эсвэл index.ts) нэг л удаа импортолно:

typescript
// main.ts — хамгийн эхний мөр байх ёстой
import "reflect-metadata";

Metadata хадгалах ба унших

Reflect.defineMetadata болон Reflect.getMetadata нь үндсэн хоёр функц юм:

typescript
import "reflect-metadata";

// Metadata бичих
// Reflect.defineMetadata(түлхүүр, утга, зорилт)
Reflect.defineMetadata("тайлбар", "Хэрэглэгчийн үйлчилгээ", ХэрэглэгчийнҮйлчилгээ);
Reflect.defineMetadata("хувилбар", "1.0.0", ХэрэглэгчийнҮйлчилгээ);

class ХэрэглэгчийнҮйлчилгээ {
  нэвтрэх(и_мэйл: string): boolean {
    return и_мэйл.includes("@");
  }
}

// Metadata унших
const тайлбар = Reflect.getMetadata("тайлбар", ХэрэглэгчийнҮйлчилгээ);
const хувилбар = Reflect.getMetadata("хувилбар", ХэрэглэгчийнҮйлчилгээ);

console.log(тайлбар); // "Хэрэглэгчийн үйлчилгээ"
console.log(хувилбар); // "1.0.0"

// Metadata байгаа эсэхийг шалгах
const байгааЭсэх = Reflect.hasMetadata("тайлбар", ХэрэглэгчийнҮйлчилгээ);
console.log(байгааЭсэх); // true

Decorator дотор metadata ашиглах

Decorator ба metadata хамтдаа ашиглахад жинхэнэ хүч гарч ирдэг. Энэ нь NestJS-ийн @Controller(), @Get() зэрэг decorator-ийн ажиллах зарчим юм:

typescript
import "reflect-metadata";

// Route metadata хадгалах decorator
function Controller(замнал: string) {
  return function (constructor: Function) {
    Reflect.defineMetadata("замнал", замнал, constructor);
  };
}

function Get(зам: string) {
  return function (target: object, propertyKey: string) {
    // Method-ийн route metadata хадгалах
    const одоогийнЗамнал: string[] =
      Reflect.getMetadata("замнал_жагсаалт", target.constructor) ?? [];
    одоогийнЗамнал.push(`GET ${зам}${propertyKey}`);
    Reflect.defineMetadata("замнал_жагсаалт", одоогийнЗамнал, target.constructor);
  };
}

@Controller("/хэрэглэгч")
class ХэрэглэгчийнController {
  @Get("/бүгд")
  бүгдийгАвах() {
    return "Бүх хэрэглэгчид";
  }

  @Get("/:id")
  нэгийгАвах() {
    return "Нэг хэрэглэгч";
  }
}

// Metadata уншиж бүртгэгдсэн route-уудыг харах
const үндсэнЗам = Reflect.getMetadata("замнал", ХэрэглэгчийнController);
const замнал = Reflect.getMetadata("замнал_жагсаалт", ХэрэглэгчийнController);

console.log("Үндсэн зам:", үндсэнЗам);
// "Үндсэн зам: /хэрэглэгч"

console.log("Route-ууд:", замнал);
// ["GET /бүгд → бүгдийгАвах", "GET /:id → нэгийгАвах"]

emitDecoratorMetadata — автомат төрлийн мэдээлэл

emitDecoratorMetadata: true тохиргоо идэвхтэй үед TypeScript method-ийн параметрийн төрлийг автоматаар metadata болгон хадгалдаг:

typescript
import "reflect-metadata";

function ТөрөлийгЛог(target: object, propertyKey: string) {
  // "design:paramtypes" — TypeScript автоматаар бичдэг metadata түлхүүр
  const параметрүүд: Function[] = Reflect.getMetadata(
    "design:paramtypes",
    target,
    propertyKey
  );

  const нэрүүд = параметрүүд?.map((т) => т.name) ?? [];
  console.log(`${propertyKey} параметрүүд:`, нэрүүд);
}

class ҮйлчилгээнийКласс {
  @ТөрөлийгЛог
  бүртгэх(нэр: string, нас: number, идэвхтэй: boolean): void {
    // ажиллах код
  }
}

// "бүртгэх параметрүүд: ['String', 'Number', 'Boolean']" гэж хэвлэнэ

Dependency injection системүүд яг энэ аргаар constructor-ийн параметрийн төрлийг мэдэж, зөв объектуудыг автоматаар дамжуулдаг.

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

TypeScript дотор хамгийн нийтлэг design pattern-уудыг — Singleton, Factory, Observer — хэрхэн хэрэгжүүлэх тухай сурна.