TypeScript / Decorator үндэс

Decorator үндэс

Decorator бол class, method, property-д нэмэлт зан төлөв (behavior) нэмэх тусгай синтакс юм. @ тэмдэгтээр эхэлдэг бөгөөд Angular, NestJS зэрэг алдартай framework-үүд decorator-ийг өргөнөөр ашигладаг.

Decorator нь нэг үгээр хэлбэл — функцийг өөрчлөхгүйгээр түүнд нэмэлт чадвар өгдөг "наалт" юм.

Decorator идэвхжүүлэх

TypeScript-д decorator туршилтын (experimental) горимд байгаа тул tsconfig.json-д идэвхжүүлэх шаардлагатай:

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

Эдгээр тохиргоогүйгээр decorator ашиглахад хөрвүүлэгч алдаа заана.

Class decorator

Class decorator нь class-ийн constructor-ийг хүлээн авч, шинэ class буцааж болдог:

typescript
// Decorator функц тодорхойлох
function Лог(constructor: Function) {
  console.log(`Class үүсгэгдлээ: ${constructor.name}`);
}

function Хөлдөөх(constructor: Function) {
  Object.freeze(constructor);
  Object.freeze(constructor.prototype);
}

// Decorator хэрэглэх — @ тэмдгийн ард нэр бичнэ
@Лог
@Хөлдөөх
class ХэрэглэгчийнҮйлчилгээ {
  нэвтрэх(и_мэйл: string) {
    console.log(`${и_мэйл} нэвтэрлээ`);
  }
}

// "Class үүсгэгдлээ: ХэрэглэгчийнҮйлчилгээ" гэж хэвлэнэ
const үйлчилгээ = new ХэрэглэгчийнҮйлчилгээ();

Decorator-ууд доороос дээш дарааллаар ажилладаг — @Хөлдөөх эхлээд, дараа нь @Лог.

Method decorator

Method decorator нь тухайн method-ийн ажиллагааг өөрчилж болдог. Хамгийн түгээмэл хэрэглээ нь — readonly болгох, лог бичих, алдааг барих:

typescript
// Method decorator — аргументүүд: target, нэр, тодорхойлолт
function Уншихад_Зориулагдсан(
  target: object,
  propertyKey: string,
  descriptor: PropertyDescriptor
) {
  descriptor.writable = false; // method-ийг дарж бичихийг хориглоно
  return descriptor;
}

function АлдааБарих(
  target: object,
  propertyKey: string,
  descriptor: PropertyDescriptor
) {
  const анхныФункц = descriptor.value;

  descriptor.value = async function (...аргументууд: unknown[]) {
    try {
      return await анхныФункц.apply(this, аргументууд);
    } catch (алдаа) {
      console.error(`${propertyKey} дотор алдаа гарлаа:`, алдаа);
      throw алдаа;
    }
  };

  return descriptor;
}

class ДатабааcийнҮйлчилгээ {
  @АлдааБарих
  async хэрэглэгчАвах(id: string) {
    // алдаа гарвал @АлдааБарих автоматаар барина
    const хариу = await fetch(`/api/users/${id}`);
    return хариу.json();
  }

  @Уншихад_Зориулагдсан
  мэндчилэх() {
    return "Сайн уу!";
  }
}

const үйлчилгээ = new ДатабааcийнҮйлчилгээ();
// үйлчилгээ.мэндчилэх = () => "Өөр зүйл"; // Алдаа! readonly

Property decorator

Property decorator нь class-ийн шинж чанарт хэрэглэгдэнэ:

typescript
// Утгыг автоматаар хэвлэдэг property decorator
function Хяналт(target: object, propertyKey: string) {
  let утга: unknown;

  Object.defineProperty(target, propertyKey, {
    get() {
      return утга;
    },
    set(шинэУтга: unknown) {
      console.log(`${propertyKey}: ${утга}${шинэУтга}`);
      утга = шинэУтга;
    },
  });
}

class ХэрэглэгчийнПрофайл {
  @Хяналт
  нэр: string = "";

  @Хяналт
  xp: number = 0;
}

const профайл = new ХэрэглэгчийнПрофайл();
профайл.нэр = "Болд";   // "нэр:  → Болд" гэж хэвлэнэ
профайл.xp = 100;        // "xp: 0 → 100" гэж хэвлэнэ

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

emitDecoratorMetadata болон Reflect Metadata API-г ашиглан decorator-д төрлийн мэдээлэл хэрхэн нэмэх тухай сурна.