Classes with Types
Intermediate · 20 min read · ▶ live playground · ✦ checkpoint
Classes in TypeScript are JavaScript classes with type checks attached: fields describe what each instance owns, constructors describe what it takes to create one, and methods describe what instances can do. The trap is that a field annotation does not create a value — strict TypeScript makes you initialize class fields before the object can be trusted.
Section 5 taught object shapes and structural typing. A class adds a runtime constructor to that world. You can call it with new, use instanceof with it, and put methods on its prototype. TypeScript then checks the same promises you already know: parameter types, return types, property types, and assignment.
Fields, constructors, and methods
A field is a property each instance is expected to have. A constructor runs when new ClassName(...) creates an instance. A method is a function stored on the class prototype and called through an instance.
The annotation locations are ordinary TypeScript:
- field annotations after the field name;
- constructor parameter annotations inside
constructor(...); - method parameter and return annotations just like Section 3 functions.
title: string and minutes: number are not variable declarations floating in space. They promise that every TrainingSession instance will have those properties. The constructor assignments are what make the promise true.
Notice the two different initialization styles. completed has a field initializer, so it is ready before the constructor body finishes. title and minutes are assigned inside the constructor from checked parameters. Both styles satisfy strict TypeScript.
Parameter properties
Sometimes a constructor parameter exists only so you can store it on the instance. TypeScript has shorthand for that: a parameter property.
constructor(private db: Database) {} means three things at once: accept a constructor parameter named db, check that it has the Database shape, and create an instance field named db from it. The private word controls who TypeScript lets read that field; 6.2 spends a full lesson on access modifiers. For now, focus on the shorthand: the parameter becomes a field without writing this.db = db.
This is still just TypeScript checking JavaScript. The Database type erases before runtime, so this is not runtime validation. It is a compile-time promise that callers must pass the right shape.
The initialization trap
With strictPropertyInitialization enabled through strict, TypeScript asks a simple question for every instance field: will this field definitely have a value when construction finishes?
Here is the classic mistake:
class SessionDraft {
title: string;
constructor() {
console.log("draft created");
}
}→ playground.ts:2:3 - error TS2564: Property 'title' has no initializer and is not definitely assigned in the constructor.
The annotation title: string says what the value must be once it exists. It does not assign a string. JavaScript will not invent "" for you, and TypeScript refuses to let the instance pretend otherwise.
The safe fix is boring and correct: initialize the field where you declare it, or assign it on every constructor path.
class SessionDraft {
title: string;
constructor(title: string) {
this.title = title;
}
label(): string {
return "draft: " + this.title;
}
}
const draft = new SessionDraft("Getters and setters");
console.log(draft.label());→ draft: Getters and setters
Getters, setters, and static members
A getter is a method you read like a property. A setter is a method that runs when you assign to a property. A static member belongs to the class value itself, not to each instance.
order.total looks like a property read, but it runs the get total() method. order.quantity = 3 looks like a property assignment, but it runs set quantity(value: number). The setter parameter annotation is the assignment contract, so "three" fails before anything runs.
The static field is read as TicketOrder.serviceFee, not order.serviceFee. Static members are useful for facts and helpers that belong to the class concept rather than one particular instance. The same annotation rules apply: static serviceFee: number = 2 is a field with an initializer and a checked type.
One last boundary: the setter's runtime if still matters. TypeScript can reject "three" because that is the wrong type, but it cannot know that 0 is invalid for your ticket order unless you write the runtime check. Types catch shape mistakes; business rules still live in code.
Next lesson turns the small private word from the parameter-property example into its own topic: public, private, protected, and the difference between TypeScript privacy and JavaScript #private.
Checkpoint
Answer all three to mark this lesson complete