Right, let’s talk about giving your classes some action. You’ve got your properties, your blueprints, but a class that just sits there holding data is about as useful as a screen door on a submarine. Methods are the functions inside your class that make things happen, and accessors (get/set) are a clever, sometimes too-clever, way to control access to your properties. They let you dress up a property access as if it’s just a simple field, while you’re secretly running a whole function behind the curtain.

The Humble Method

A method is just a function that lives on a class and has access to its instance via this. Don’t overcomplicate it. Here’s the most basic form:

class Spaceship {
  name: string;
  fuel: number = 100;

  // A method
  launch() {
    if (this.fuel > 0) {
      console.log(`3... 2... 1... Liftoff! ${this.name} is away!`);
      this.fuel = 0; // We're going all-in on this launch.
    } else {
      console.log("Abort! Abort! Not enough fuel!");
    }
  }
}

const enterprise = new Spaceship();
enterprise.name = "Enterprise";
enterprise.launch(); // Output: 3... 2... 1... Liftoff! Enterprise is away!

The beauty here is encapsulation. The launch method has the logic to check fuel levels. The outside world just calls .launch() and trusts the class to handle the details. It doesn’t need to manually set fuel to zero afterward. This is Object-Oriented Programming 101: bundle the data with the operations on that data.

The get Accessor (The Illusionist)

Now, the get accessor. It lets you define a function that is executed when someone accesses a property. It’s perfect for deriving computed values on the fly. Why calculate something and store it if you might not need it? Calculate it when asked.

class Rectangle {
  constructor(public width: number, public height: number) {}

  // This looks like a property, but it's a function masquerading as one.
  get area() {
    return this.width * this.height;
  }
}

const myRect = new Rectangle(10, 5);
console.log(myRect.area); // Output: 50
// Notice we didn't write myRect.area() — no parentheses!

See the magic? You use myRect.area, not myRect.area(). The get keyword makes it behave like a property, not a method. This is fantastic for read-only values that are computed from other internal state. The user gets a clean interface, and you get to run logic.

The set Accessor (The Bouncer)

The set accessor is get’s often-troublesome sibling. It’s a function that runs when someone tries to assign a value to a property. Its primary job is to be a gatekeeper, validating or transforming input before it’s stored.

class Thermometer {
  private _celsius: number = 0;

  get celsius(): number {
    return this._celsius;
  }

  set celsius(value: number) {
    // The bouncer logic: Absolute zero is the hard limit.
    if (value < -273.15) {
      console.log("Value cannot be below absolute zero. Setting to -273.15.");
      this._celsius = -273.15;
    } else {
      this._celsius = value;
    }
  }

  get fahrenheit(): number {
    return (this._celsius * 9) / 5 + 32;
  }
}

const temp = new Thermometer();
temp.celsius = 25; // This calls the 'set' accessor
console.log(temp.fahrenheit); // 77 - calls the 'get' accessor

temp.celsius = -300; // Calls the 'set' accessor, which sees the invalid value
console.log(temp.celsius); // Output: -273.15

This is where the pattern shines. The user interacts with a simple temp.celsius = -300, but you’ve intercepted that assignment and enforced the laws of physics. Notice the private _celsius property? That’s the convention. The public-facing property is celsius (controlled by the accessors), while the actual data is stored in _celsius.

The Pitfalls and The “Why”

  1. Unexpected Side Effects: The biggest trap with get/set is that they look like simple property access but can be doing heavy computational work or having side effects. A user doesn’t expect writing obj.value = 5 to trigger a database write or a complex calculation. If you need to do something, use a clear method like .updateValue(5). Use set only for validation/transformation on the way to a backing field.

  2. The TypeScript Compiler Note: You must specify the same type for both the get and set accessor if you use them together. The get is what you get back, the set is what you put in. They need to be compatible. TypeScript will yell at you if they aren’t.

  3. Read-Only is Easy: If you only define a get accessor without a set, the property effectively becomes read-only. This is a fantastic and clean way to expose derived data.

class ReadOnlyExample {
  private _secret = 42;

  get secret() {
    return this._secret;
  }
}

const obj = new ReadOnlyExample();
console.log(obj.secret); // 42
obj.secret = 100; // COMPILER ERROR: Cannot assign to 'secret' because it is a read-only property.

So, when should you use them? Use get for elegant computed properties. Use set sparingly, almost exclusively for validation. They are a powerful tool for creating a robust and intuitive API for your classes, but like any power tool, misuse it and you’ll end up with a bloody mess of unexpected behavior.