Static Members
- #typescript
- #classes
- #static
- #oop
In questa lezione
1. Introduction
Every property and method you’ve seen so far in the Classes lesson belongs to an instance — each object created with new gets its own independent copy of the instance fields. The static keyword flips that: a static member belongs to the class itself, not to any particular instance. There is exactly one copy of it, shared by everyone.
Think of the difference this way: instance members answer “what does this specific object know?”, while static members answer “what does the class as a whole know?“.
class Dog {
static instanceCounter: number = 0;
private name: string;
public constructor(name: string) {
Dog.instanceCounter++;
this.name = name;
}
static decreaseCount() {
this.instanceCounter--;
}
}
2. Accessing static members
Static members are accessed through the class name, never through an instance variable:
const dog1 = new Dog("Franco"); // instanceCounter = 1
console.log(Dog.instanceCounter);
const dog2 = new Dog("Bruschetta"); // instanceCounter = 2
console.log(Dog.instanceCounter);
Dog.decreaseCount();
console.log(Dog.instanceCounter); // 1
Notice that dog1.instanceCounter is not how you’d read this value — it doesn’t belong to dog1, it belongs to Dog. In fact, TypeScript will flag dog1.instanceCounter as an error: instance variables cannot see static members through this.
Attenzione
A common beginner mistake is expecting each instance to carry its own copy of a static field. It doesn’t — every instance of Dog shares the exact same instanceCounter. Incrementing it from any constructor call affects the value every other instance (and the class itself) will see.
3. this inside a static method
Inside the constructor, Dog.instanceCounter++ explicitly names the class. But inside decreaseCount(), the code uses this.instanceCounter-- instead. Why does that work?
Inside a static method, this refers to the class itself (the constructor function), not to an instance — because static methods are never called on an instance in the first place (Dog.decreaseCount(), not dog1.decreaseCount()). So this.instanceCounter inside a static method is equivalent to Dog.instanceCounter.
Suggerimento
Using this instead of the hardcoded class name inside static methods has a practical benefit: if a subclass inherits the static method, this will correctly refer to the subclass, not the base class. This matters for patterns like static factory methods that need to work correctly across an inheritance chain.
4. Static properties vs instance properties
class Counter {
static total: number = 0; // one copy, shared by the whole class
private id: number; // one copy per instance
constructor() {
Counter.total++;
this.id = Counter.total;
}
public getId(): number {
return this.id;
}
}
const a = new Counter();
const b = new Counter();
console.log(a.getId()); // 1
console.log(b.getId()); // 2
console.log(Counter.total); // 2
id is different for every Counter instance (each one remembers its own creation order), while total is one single number that every instance contributed to and that lives on the class, not on any object.
5. Common use cases for static members
- Counters — tracking how many instances of a class have been created (as in the
Dogexample above). - Factory methods — a static method that constructs and returns instances, often validating input first:
class User { private constructor(public name: string) {} static create(name: string): User { if (!name.trim()) { throw new Error("Name cannot be empty"); } return new User(name); } } const u = User.create("Dennis"); - Shared caches or registries — a static
Mapshared by every instance to look up or memoize values. - Constants tied to a class — e.g.
static readonly MAX_SPEED = 300;on aCarclass, since the value belongs conceptually to the type, not any one car.
Nota
Note the private constructor in the factory example: combining a private constructor with a public static method is a common pattern to force all object creation to go through validated, controlled entry points instead of a bare new User(...).
6. Further reading
- TypeScript Handbook — Classes: Static Members
- MDN — static keyword (JavaScript classes)
- TypeScript Playground
7. Quiz
Mettiti alla prova
0/6 risposte
What does the
statickeyword mean when applied to a class member?How should you access a static property named
totalon a classCounter?Inside a static method, what does
thisrefer to?If
Dog.instanceCounteris incremented in the constructor every timenew Dog(...)runs, what happens to previously created instances?Which of these is a typical real-world use case for a static method?
Why might combining a
private constructorwith astatic create()method be useful?
8. Exercises
8.1 Exercise
Scenario: You’re building a simple ticketing system where every issued ticket needs a unique, sequential ID.
Task:
- Create a class
Ticketwith astatic nextId: number = 1and aprivate readonly id: number. - In the constructor, assign
this.id = Ticket.nextIdand then incrementTicket.nextId. - Add a public method
getId(): numberand a static methodstatic getIssuedCount(): numberthat returns how many tickets have been issued so far. - Create several
Ticketinstances and print each one’s ID plus the total issued count.
Learning objective: understand how static fields maintain shared state across all instances, distinct from per-instance fields.
8.2 Exercise
Scenario: You want a small in-memory cache shared by every instance of a Config class, so expensive lookups aren’t repeated.
Task:
- Create a class
Configwith aprivate static cache: Map<string, string> = new Map(). - Add a static method
static getOrCompute(key: string, compute: () => string): stringthat returns the cached value if present, otherwise computes it withcompute(), stores it in the cache, and returns it. - Call
getOrComputetwice with the same key but acomputecallback that logs"computing..."— verify the log only appears once. - Explain in a comment why
cacheneeded to bestaticrather than an instance field for this to work.
Learning objective: apply static members to implement a shared cache pattern and clarify the difference between per-instance and per-class state.