Skip to content
Theory · Classes · lesson 7 of 12

get, set and static

A thermostat hangs on the wall of a house built from our blueprint. Its display shows a number, and a knob changes it. But a mechanism works behind the display: the knob will not turn below the allowed limit, and the display can show the temperature in other units too. JavaScript's get and set methods work the same way: from outside they look like an ordinary property, while code runs on every read and write.

TypeScript adds types to them:

TypeScript
class Temperature {
    #celsius = 0;

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

    set celsius(value: number) {
        this.#celsius = Math.max(value, -273.15);
    }
}

const t = new Temperature();
t.celsius = -300;
console.log(t.celsius); // -273.15

Let's go through the example line by line:

  1. The field #celsius holds the value, and it is not visible from outside.
  2. get celsius(): number is a getter. To code outside, t.celsius looks like a field of type number.
  3. set celsius(value: number) is a setter. The write t.celsius = -300 calls it, and it keeps the value from going below absolute zero.
  4. The program prints -273.15.

Types on get and set

A getter is given a result type, a setter a parameter type. tsc checks every read and every write against them. For example, t.celsius = "20" does not pass: the setter expects a number.

TEXT
error TS2322: Type 'string' is not assignable to type 'number'.

If you leave out the type of the setter's parameter, TypeScript takes it from the result of the getter with the same name.

Here the thermostat picture stops working. A display shows a number stored somewhere, while a getter stores nothing: it runs its code again on every read.

Only get

A property with a getter but no setter is a read-only property to tsc:

TypeScript
class Circle {
    radius = 1;

    get area(): number {
        return Math.PI * this.radius ** 2;
    }
}

const c = new Circle();
c.area = 10; // ✗
TEXT
error TS2540: Cannot assign to 'area' because it is a read-only property.

It is the same error as for a write to a readonly field. What happens at run time depends on how you run the code. In the JavaScript tsc writes, the first line is "use strict";, and the program stops on the line c.area = 10:

TEXT
TypeError: Cannot set property area of #<Circle> which has only a getter

node file.ts does not add the "use strict"; line, and there the write is silently lost: c.area still returns the circle's area.

static members

Now the lesson's second, separate tool: it has nothing to do with get and set. Back to the blueprint. A note can be kept on the blueprint itself, not on any house: "12 houses have been built from this blueprint". There is one such note for the whole blueprint. A static member of a class works the same way: it belongs to the class itself, and there is only one, however many objects are created.

TypeScript
class Ticket {
    static issued = 0;
    id: number;

    constructor() {
        Ticket.issued += 1;
        this.id = Ticket.issued;
    }
}

const a = new Ticket();
const b = new Ticket();
console.log(a.id, b.id, Ticket.issued); // 1 2 2
  1. static issued = 0 is one field for the whole class; its type number is inferred from the value.
  2. The constructor increases the shared counter through the class's name: Ticket.issued += 1.
  3. Each ticket has its own ordinary field id: it gets the counter's current value.
  4. The program prints 1 2 2: the tickets have the numbers 1 and 2, while there is one counter, and it holds 2.

Methods can be static too. Inside a static method this is the class itself, so static reset(): void { this.issued = 0; } resets the shared counter.

Interviewers often ask how a static member differs from an instance member. The answer: a static member is one for the whole class, and it is accessed through the class's name. Each object has its own instance member, new creates it, and inside methods it is accessed through this.

Common mistake

The static field was accessed through this, like an ordinary one:

TypeScript
class Ticket {
    static issued = 0;

    constructor() {
        this.issued += 1; // ✗
    }
}
TEXT
error TS2576: Property 'issued' does not exist on type 'Ticket'. Did you mean to access the static member 'Ticket.issued' instead?

In the constructor this is the new ticket, while the counter hangs on the blueprint, not on the house. The message itself suggests the fix: write Ticket.issued.

Key points

  • A get is given a result type, a set a parameter type, and tsc checks every read and write against them.
  • A property with only a get is read-only: a write gives error TS2540.
  • A static member is one for the whole class, and it is accessed through the class's name: Ticket.issued.
  • A static member is not visible through an instance: error TS2576; and in a static method this is the class itself.