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 суулгах хэрэгтэй:
npm install reflect-metadata
Дараа нь tsconfig.json-д тохиргоо хийнэ:
{
"compilerOptions": {
"target": "ES2020",
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
Програмынхаа хамгийн эхний файлд (main.ts эсвэл index.ts) нэг л удаа импортолно:
// main.ts — хамгийн эхний мөр байх ёстой
import "reflect-metadata";
Metadata хадгалах ба унших
Reflect.defineMetadata болон Reflect.getMetadata нь үндсэн хоёр функц юм:
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-ийн ажиллах зарчим юм:
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 болгон хадгалдаг:
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 — хэрхэн хэрэгжүүлэх тухай сурна.