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:
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.15Let's go through the example line by line:
- The field
#celsiusholds the value, and it is not visible from outside. get celsius(): numberis a getter. To code outside,t.celsiuslooks like a field of typenumber.set celsius(value: number)is a setter. The writet.celsius = -300calls it, and it keeps the value from going below absolute zero.- 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.
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:
class Circle {
radius = 1;
get area(): number {
return Math.PI * this.radius ** 2;
}
}
const c = new Circle();
c.area = 10; // ✗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:
TypeError: Cannot set property area of #<Circle> which has only a getternode 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.
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 2static issued = 0is one field for the whole class; its typenumberis inferred from the value.- The constructor increases the shared counter through the class's name:
Ticket.issued += 1. - Each ticket has its own ordinary field
id: it gets the counter's current value. - The program prints
1 2 2: the tickets have the numbers1and2, while there is one counter, and it holds2.
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:
class Ticket {
static issued = 0;
constructor() {
this.issued += 1; // ✗
}
}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
getis given a result type, aseta parameter type, andtscchecks every read and write against them. - A property with only a
getis read-only: a write gives errorTS2540. - A
staticmember is one for the whole class, and it is accessed through the class's name:Ticket.issued. - A
staticmember is not visible through an instance: errorTS2576; and in astaticmethodthisis the class itself.