Fields and the constructor
What you'll learn:
- declare class fields with types and find a field that was left without a value;
- close fields with
private,protectedand#and know which of them works at run time; - protect fields with
readonlyand shorten a constructor with parameter properties; - override methods with
overrideand set shared rules withabstractandimplements.

An architect draws the blueprint of a house: how many rooms it has, where the windows are, how big the kitchen is. A whole block of houses is built from one blueprint, and every house in it is laid out the same way. A class is such a blueprint, and the objects that new creates are the houses built from it.
In JavaScript the blueprint can be incomplete. A field appears in an object when the constructor or a method writes something into this. Misspell a field's name inside a method, and JavaScript quietly creates a new field with the typo in its name.
In TypeScript every field is listed in the class body, and each one has a type. The compiler works like a building inspector: it checks every use of a field against the blueprint. Here is a click counter:
class Counter {
count: number;
step = 1;
constructor(start: number) {
this.count = start;
}
next(): number {
this.count += this.step;
return this.count;
}
}
console.log(new Counter(10).next()); // 11Let's go through the class line by line:
count: number;declares the fieldcountwith the typenumber. There is no value here; the constructor gives it one.step = 1;declares a field together with its value. TypeScript infers the type from the value:number.- The constructor takes
start: numberand puts it intothis.count. - The method
next(): numberadds the step and returns the new value. Its result type is checked as for an ordinary function. new Counter(10).next()prints11.
A field with a type
A field declaration looks like a variable declaration, only without let:
- the field's name:
count; - a colon and the type:
: number, the same annotation as on a variable; - an initial value after
=: an optional part, like= 1on the fieldstep.
When there is a value, you may leave the type out: TypeScript infers it. That is why step has no annotation, and a field label = "clicks" would get the type string.
Inside the methods tsc knows the type of every field. It will not let this.count = "many" through, in the same way as it would not allow that assignment to a variable of type number.
Every field gets a value
Strict mode checks one more thing: by the end of the constructor every field must have a value. Let's remove the constructor from the counter:
class Counter {
count: number; // ✗
step = 1;
}
console.log(new Counter().step);error TS2564: Property 'count' has no initializer and is not definitely assigned in the constructor.The compiler says: "the property count has no initial value, and the constructor does not definitely assign it". In JavaScript such a field would be undefined, while its type promises a number. The error catches that mismatch before the program runs.
There are three ways to fix it:
- give the field a value in its declaration:
count = 0;; - assign it a value in the constructor, as in the first example;
- allow the field to be empty: the field
count?: number;has the typenumber | undefined, and you have to check it before use.
The word "definitely" in the message matters. An assignment inside an if does not count: if the condition fails, the field stays empty. Nor does an assignment in a method the constructor calls: tsc looks only at the constructor itself.
A class is also a type
You can write a class's name wherever a type goes: on a variable, on a parameter, on a function's result.
class Counter {
count = 0;
step = 1;
}
function show(c: Counter): string {
return `${c.count} (+${c.step})`;
}
console.log(show(new Counter())); // 0 (+1)
console.log(show({ count: 5, step: 2 })); // 5 (+2)showtakes a parameter of typeCounterand reads its fields.- The first call passes an object created with
new Counter()and prints0 (+1). - The second call passes a plain object with no
newat all, andtsclets it through too. The program prints5 (+2).
Here the blueprint picture stops working. A real inspector would ask which blueprint the house was built from. TypeScript compares only the shape: any object with the fields count and step of the right types fits. This is the same structural typing that interfaces have.
Common mistake
The field was not declared but written straight in the constructor, the way you are used to in JavaScript:
class Counter {
constructor(start: number) {
this.count = start; // ✗
}
}error TS2339: Property 'count' does not exist on type 'Counter'.In JavaScript a write to this creates the field on the fly. TypeScript checks the blueprint: the class body has no field count, so nothing can be written to it. Add the declaration count: number; above the constructor. The same check catches typos: for this.cuont the compiler says there is no such property and suggests count.
Key points
- Class fields are declared in the class body with a type, for example
count: number;, and a field with a value gets its type inferred. - In strict mode every field gets a value in its declaration or in the constructor, otherwise you get error
TS2564. - You cannot write a field to
thisthat is not among the declared ones: errorTS2339. - A class's name is also a type, and any object of the same shape fits it.