Classes with Typed Properties
You wrote ES6 classes in Intermediate JavaScript. In TypeScript, classes gain explicit property declarations with types, and the compiler verifies that every instance is constructed correctly and that every method is called with the right arguments.
Declaring class properties
Section titled “Declaring class properties”TypeScript requires class properties to be declared before they are used. The declaration goes at the top of the class body:
class QuizEngine { questions: TriviaQuestion[]; index: number; score: number;
constructor(questions: TriviaQuestion[]) { this.questions = questions; this.index = 0; this.score = 0; }}The declarations tell TypeScript what properties exist on every instance and what their types are. Accessing an undeclared property anywhere in the class is a compile error.
Typed constructors
Section titled “Typed constructors”The constructor is typed like any other function — parameters are annotated, and TypeScript checks that every required property is initialized:
class QuizEngine { questions: TriviaQuestion[]; index: number = 0; score: number = 0;
constructor(questions: TriviaQuestion[]) { this.questions = questions; // index and score have inline defaults — no assignment needed }}Inline defaults (= 0) eliminate the assignment in the constructor body. TypeScript knows the property is initialized and will not report an error about uninitialized properties (which strict: true enforces via the strictPropertyInitialization rule).
Typed methods
Section titled “Typed methods”Methods are annotated the same way as standalone functions:
class QuizEngine { // ...
get total(): number { return this.questions.length; }
answer(selected: string): boolean { const correct = this.questions[this._index]?.correctAnswer === selected; if (correct) this.score++; this.index++; return correct; }}total is a getter — it returns number. answer takes a string and returns a boolean. TypeScript checks every call to these methods against their signatures.
Getters and setters
Section titled “Getters and setters”Getters are typed with a return type after the closing parenthesis:
get isDone(): boolean { return this.index >= this.questions.length;}
get current(): TriviaQuestion | null { return this.questions[this.index] ?? null;}current returns TriviaQuestion | null — when the index is out of bounds, the expression evaluates to undefined, and ?? null converts that to null. TypeScript infers undefined from optional chaining and null from the nullish coalescing fallback, but an explicit return type makes the contract visible.
Static methods
Section titled “Static methods”Static methods belong to the class itself, not to instances. They are typed the same way as instance methods:
class QuizEngine { static loadHighScore(): HighScore | null { const raw = localStorage.getItem('aceit_highscore'); if (!raw) return null; try { return JSON.parse(raw) as HighScore; } catch { return null; } }
static saveHighScore(score: number, total: number): void { const entry: HighScore = { score, total, date: new Date().toLocaleDateString() }; localStorage.setItem('aceit_highscore', JSON.stringify(entry)); }
static isNewHighScore(score: number, total: number): boolean { const existing = QuizEngine.loadHighScore(); if (!existing) return true; return score / total > existing.score / existing.total; }}QuizEngine.loadHighScore() returns HighScore | null — the caller must handle both cases. TypeScript enforces this wherever the return value is used.
How TypeScript treats a class
Section titled “How TypeScript treats a class”A class in TypeScript serves double duty:
- A runtime value — the class itself, callable as a constructor.
- A type — the shape of an instance, usable anywhere a type is expected.
function displayResult(engine: QuizEngine): void { console.log(`${engine.currentScore} / ${engine.total}`);}Here QuizEngine is used as a type annotation — TypeScript checks that the passed value is a QuizEngine instance with all the expected properties and methods.
Exercise
Section titled “Exercise”In quiz.ts, write the skeleton of the QuizEngine class:
- Declare properties:
questions: TriviaQuestion[],_index: number = 0,_score: number = 0. - Write a constructor that accepts
questions: TriviaQuestion[]and assigns it. - Add getters:
total: number,index: number,currentScore: number,isDone: boolean,current: TriviaQuestion | null. - Add method
answer(selected: string): boolean. - Add static methods
loadHighScore,saveHighScore, andisNewHighScoreas shown above.
Run npx tsc --noEmit and fix all errors before moving to the next lesson.
- Class properties are declared at the top of the class body with a type — accessing undeclared properties is an error.
- Inline defaults (
= 0) initialize properties without a constructor assignment. - Getters, setters, instance methods, and static methods are all annotated the same way as standalone functions.
- A class in TypeScript is both a runtime value and a type — the type describes the shape of an instance.
strict: trueenforces that every declared property is initialized in the constructor or via a default.