DEV Community

harmonyPreacher
harmonyPreacher

Posted on

HarmonyOS 5 Decorator Principles and Custom Decorator Guide

HarmonyOS 5 Decorator Principles and Custom Decorator Guide

In HarmonyOS development with ArkTS, decorators provide a powerful and concise way to implement component logic, manage state, and customize behavior. This guide explains what decorators are, how they work internally, and how to create custom decorators within the constraints of the ArkTS language.


I. What Is a Decorator in HarmonyOS?

In ArkTS (an extension of TypeScript), a decorator is a special kind of declaration that can annotate and modify classes, methods, and properties. It is commonly used for metaprogramming — enhancing functionality without altering the original code logic.

Common Built-in HarmonyOS Decorators

  • Component decorators: @Component, @Entry, @Builder, @CustomDialog, etc.
  • State decorators: @State, @Prop, @Link, @Observed, @ObservedV2, etc.

Example:

@Component
struct MyComponent {
  @State count: number = 0;
}
Enter fullscreen mode Exit fullscreen mode

II. Internal Principles of Decorators in ArkTS

ArkTS compiles decorators into function calls that wrap the target object. These function calls can modify or enhance the behavior of the class, method, or property being decorated.

1. Class Decorators

Applied above a class declaration. They receive the constructor as a parameter.

function logDecorator(constructor: Function) {
  console.log(`Class ${constructor.name} is created.`);
}

@logDecorator
class MyComponent {
  constructor() {
    console.log('MyComponent instance is created.');
  }
}
Enter fullscreen mode Exit fullscreen mode

2. Method Decorators

Used to wrap or alter the behavior of class methods. Useful for logging, throttling, or debouncing:

export function debounce(timeout: number) {
  return function (target: Object, propKey: string, descriptor: PropertyDescriptor) {
    let timer: number = 0;
    const original = descriptor.value;
    descriptor.value = function (...args: any[]) {
      timer && clearTimeout(timer);
      timer = setTimeout(() => original.apply(this, args), timeout);
    };
  };
}

export function throttle(wait: number) {
  return function (target: Object, propKey: string, descriptor: PropertyDescriptor) {
    let last = 0;
    const original = descriptor.value;
    descriptor.value = function (...args: any[]) {
      const now = Date.now();
      if (now - last >= wait) {
        original.apply(this, args);
        last = now;
      }
    };
  };
}
Enter fullscreen mode Exit fullscreen mode

🛑 Note: ArkTS does not support any or unknown. Use Object as a workaround.

3. Property Decorators

Applied to class properties. Can be used to add validation or reactive logic.

function positiveNumber(target: Object, propertyKey: string) {
  let value: number;
  Object.defineProperty(target, propertyKey, {
    get: () => value,
    set: (newValue: number) => {
      if (newValue < 0) {
        throw new Error(`${propertyKey} must be a positive number.`);
      }
      value = newValue;
    },
    enumerable: true,
    configurable: true
  });
}

class MyModel {
  @positiveNumber
  age: number = 20;
}

const model = new MyModel();
model.age = -1; // Throws error
Enter fullscreen mode Exit fullscreen mode

III. How to Define Custom Decorators in ArkTS (HarmonyOS)

Because ArkTS enforces strong typing and disallows any, custom decorators must follow stricter conventions.

Method Decorator Example

function methodLogger(target: Object, key: string, descriptor: PropertyDescriptor) {
  const originalMethod: Function = descriptor.value;
  descriptor.value = (...args: Object[]) => {
    console.log(`Calling ${key} with args: ${args}`);
    const result = originalMethod(...args);
    console.log(`Returned from ${key}: ${result}`);
    return result;
  };
  return descriptor;
}
Enter fullscreen mode Exit fullscreen mode

IV. Practical Example: Custom Method Decorator in a Component

@Entry
@Component
struct DecoratorDemoComponent {
  @State message: string = 'Hello HarmonyOS';

  @methodLogger
  private processData(input: string): string {
    console.log('Processing data...');
    return `Processed Data: ${input.toUpperCase()}`;
  }

  aboutToAppear() {
    const result = this.processData('decorator demo');
    console.log('Final Result:', result);
    this.message = result;
  }

  build() {
    Column({ space: 30 }) {
      Text(this.message)
        .fontSize(24)
        .margin(10);

      Button('Click to Trigger Method')
        .onClick(() => this.processData('button click'))
        .margin(10);
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center);
  }
}
Enter fullscreen mode Exit fullscreen mode

V. Summary & Recommendations

  • ArkTS decorators are essential in HarmonyOS development for enhancing components, managing state, and abstracting logic.
  • Avoid any and use Object for maximum compatibility.
  • Method decorators like @debounce and @throttle improve performance and control.
  • When creating custom decorators, always respect ArkTS' strict typing system.

Recommended Practices:

Decorator Type Use Case Notes
@State Local reactive state Re-renders on update
@Prop Receive immutable data One-way data flow

Top comments (0)