TypeScript Classes
A guide to TypeScript classes, constructors, access modifiers, and inheritance with practical examples.
TL;DR
- 01Create classes with typed properties and a constructor.
- 02Control access with public, private, and protected modifiers.
- 03Extend classes to share behavior through inheritance.
Tips
- 01Prefer parameter properties in constructors to cut boilerplate, since they declare and assign in a single line.
Warnings
- 01Use the <code>#</code> prefix for true privacy when needed, since the <code>private</code> keyword is only enforced at compile time.
Basic Syntax
- Declare a class with the
classkeyword and a PascalCase name.class User { name: string; constructor(name: string) { this.name = name; } } - Declare typed properties at the top of the class body for clarity.
- The constructor runs automatically when you create a new instance.
const sam = new User("Sam"); - Use
thisinside methods to refer to the current instance. - Methods are typed just like regular functions with parameters and return types.
Constructor Shortcuts
- Add modifiers to constructor parameters to create and assign properties at once.
class User { constructor(public name: string, public age: number) {} } - This shortcut saves you from writing property declarations separately.
- Combine with
readonlyto make the property immutable after construction.class Point { constructor(readonly x: number, readonly y: number) {} } - Use default values in the constructor for optional inputs.
- Parameter properties work with
public,private,protected, andreadonly.
Access Modifiers
- Use
publicfor properties that anyone can read or change.class User { public name: string = "Sam"; } - Use
privateto hide properties from code outside the class.class Account { private balance: number = 0; } - Use
protectedto allow access from the class and its subclasses only. - The
#prefix gives true runtime privacy that survives compilation.class Counter { #count = 0; next() { return ++this.#count; } } - Default modifier is
publicwhen none is specified.
Inheritance
- Use the
extendskeyword to create a subclass of another class.class Animal { move() { console.log("moving"); } } class Dog extends Animal { bark() { console.log("woof"); } } - Call
super()inside the constructor to run the parent constructor first.class Cat extends Animal { constructor(public name: string) { super(); } } - Override parent methods by redeclaring them in the subclass.
- Use
super.method()to call the parent method from inside an override. - A class can extend only one parent — use interfaces for multiple contracts.
Abstract and Static
- Mark a class
abstractto prevent direct instantiation.abstract class Shape { abstract area(): number; } - Subclasses must implement every abstract method to be usable.
- Use
staticto attach properties or methods to the class itself, not instances.class MathHelper { static double(n: number) { return n * 2; } } MathHelper.double(5); // 10 - Static methods are great for utility functions related to the class.
- Use
implementsto require a class to match an interface contract.
FAQ
Abstract classes can contain implemented methods and constructor logic, while interfaces only define shape contracts with no implementation. Use abstract classes when you want to share code across subclasses; use interfaces when you only need to enforce a structure.
Static members belong to the class itself rather than any instance, so you access them via the class name (e.g., MyClass.count). They're useful for factory methods, counters, or shared utilities that don't depend on instance state.
Use protected when subclasses need direct access to a member, and private when you want to completely hide implementation details from child classes. Choosing protected too liberally can couple subclasses tightly to parent internals, making refactoring harder.
Use super() in the subclass constructor before accessing this, and super.methodName() to invoke an overridden parent method. Forgetting to call super() in a derived constructor is a runtime error, not just a type error.
Yes — a class can implement multiple interfaces by separating them with commas (e.g., class Foo implements Bar, Baz). This is the standard way to satisfy multiple contracts since TypeScript only supports single class inheritance.