Getters, Setters & Static Members
Getters and setters let a property run code when it's read or written, so you can compute values or validate input behind an ordinary-looking property. Static members belong to the class itself rather than to each instance.
Getters
A getter looks like a method prefixed with get, but callers use it like a property — no parentheses:
class Rectangle { constructor(public width: number, public height: number) {} get area(): number { return this.width * this.height; } } const r = new Rectangle(3, 4); console.log(r.area); r.width = 10; console.log(r.area);
12 40
The area is always computed from the current width and height, so it can never get out of sync. A getter with no matching setter is automatically treated as readonly — assigning to r.area is a compile error.
Setters with validation
A setter runs whenever the property is assigned, which makes it a natural place to validate:
class Thermostat { private _celsius = 20; get celsius() { return this._celsius; } set celsius(value: number) { if (value < 5 || value > 30) { throw new RangeError(`Out of range: ${value}`); } this._celsius = value; } get fahrenheit() { return this._celsius * 9 / 5 + 32; } } const t = new Thermostat(); t.celsius = 25; console.log(t.celsius, t.fahrenheit); try { t.celsius = 99; } catch (e) { console.log((e as Error).message); }
25 77 Out of range: 99
The actual value lives in a private backing field (by convention prefixed with _, or use a # field). Outside code just sees a normal celsius property.
string | number while the getter always returns number. Most of the time, keeping them the same is clearer.Static properties and methods
static members are attached to the class, not to instances. You access them through the class name:
class IdGenerator { static prefix = "user"; private static next = 1; static create(): string { return `${IdGenerator.prefix}-${IdGenerator.next++}`; } } console.log(IdGenerator.create()); console.log(IdGenerator.create()); IdGenerator.prefix = "admin"; console.log(IdGenerator.create());
user-1 user-2 admin-3
Static members can be private, protected and readonly too. A private static counter like next is shared by everything that uses the class, but hidden from outside code.
Static factory methods
A common use of static is a named alternative to the constructor:
class Money { private constructor(readonly cents: number) {} static fromDollars(dollars: number) { return new Money(Math.round(dollars * 100)); } static zero() { return new Money(0); } add(other: Money) { return new Money(this.cents + other.cents); } toString() { return `$${(this.cents / 100).toFixed(2)}`; } } const total = Money.fromDollars(4.1).add(Money.fromDollars(0.2)); console.log(String(total));
$4.30
Making the constructor private forces everyone to go through the factories, which guarantees money is always stored as whole cents. Calling new Money(5) from outside the class is a compile error.
Static blocks
For setup that's more than a single expression, a static { } block runs once when the class is defined:
class Config { static readonly values: Record<string, string> = {}; static { const pairs = "mode=prod;region=eu".split(";"); for (const p of pairs) { const [k, v] = p.split("="); Config.values[k] = v; } } } console.log(Config.values);
{ mode: 'prod', region: 'eu' }Instance vs static at a glance
| Instance member | Static member | |
|---|---|---|
| Declared as | count = 0 | static count = 0 |
| Accessed via | obj.count / this.count | ClassName.count |
| One copy per | Object | Class |
| Good for | State of one object | Factories, shared config, counters, helpers |