Skip to content

auto bind methods

awekrx edited this page May 29, 2026 · 1 revision

auto-bind-methods

Import

import { autoBindMethods } from '@dev-suite/decorators/auto-bind-methods'

Category

  • class

Use Case

Keep class method references bound to the instance when methods are passed as callbacks.

Replaces

  • Manual .bind(this) calls in constructors
  • Arrow-function class fields used only to preserve this

Example 1

Without decorator

class SocketClient {
  private readonly ws: WebSocket;
  private reconnectAttempts = 0;

  constructor(ws: WebSocket) {
    this.ws = ws;

    // Every callback method must be bound manually.
    this.onOpen = this.onOpen.bind(this);
    this.onClose = this.onClose.bind(this);
    this.onMessage = this.onMessage.bind(this);
  }

  attach() {
    this.ws.addEventListener('open', this.onOpen);
    this.ws.addEventListener('close', this.onClose);
    this.ws.addEventListener('message', this.onMessage);
  }

  private onOpen() {
    this.reconnectAttempts = 0;
  }

  private onClose() {
    this.reconnectAttempts += 1;
  }

  private onMessage(event: MessageEvent) {
    console.log(event.data);
  }
}

With decorator

import { autoBindMethods } from '@dev-suite/decorators/auto-bind-methods';

@autoBindMethods()
class SocketClient {
  private readonly ws: WebSocket;
  private reconnectAttempts = 0;

  constructor(ws: WebSocket) {
    this.ws = ws;
  }

  attach() {
    // Bound methods are safe to pass directly.
    this.ws.addEventListener('open', this.onOpen);
    this.ws.addEventListener('close', this.onClose);
    this.ws.addEventListener('message', this.onMessage);
  }

  private onOpen() {
    this.reconnectAttempts = 0;
  }

  private onClose() {
    this.reconnectAttempts += 1;
  }

  private onMessage(event: MessageEvent) {
    console.log(event.data);
  }
}

Why better

  • Removes constructor bind boilerplate
  • Makes callback safety explicit at class declaration

Example 2

Without decorator

class SearchController {
  private value = '';

  constructor(private readonly input: HTMLInputElement) {
    this.onInput = this.onInput.bind(this);
    this.onBlur = this.onBlur.bind(this);
  }

  mount() {
    this.input.addEventListener('input', this.onInput);
    this.input.addEventListener('blur', this.onBlur);
  }

  private onInput(event: Event) {
    this.value = (event.target as HTMLInputElement).value;
  }

  private onBlur() {
    console.log('final value:', this.value);
  }
}

With decorator

import { autoBindMethods } from '@dev-suite/decorators/auto-bind-methods';

@autoBindMethods()
class SearchController {
  private value = '';

  constructor(private readonly input: HTMLInputElement) {}

  mount() {
    this.input.addEventListener('input', this.onInput);
    this.input.addEventListener('blur', this.onBlur);
  }

  private onInput(event: Event) {
    this.value = (event.target as HTMLInputElement).value;
  }

  private onBlur() {
    console.log('final value:', this.value);
  }
}

Why better

  • Same policy reused for another UI flow without extra binding code
  • Reduces risk of this bugs when handlers are detached/passed around

Clone this wiki locally