class Account {
public balance: number = 0;
private pin: string = '1234';
protected accountType: string = 'checking';
}
Topics
26
Classes: Access Modifiers, Abstract Classes & Implements
Conditional Types
Declaration Files
Decorators
Discriminated Unions & Exhaustiveness Checking
Enums
Function Types, Overloads & Optional/Default Parameters
Generics
Index Signatures & Record Types
Mapped Types
Migrating JavaScript to TypeScript
Modules
Namespaces & Declaration Merging
Never, Unknown & Void Types
Nullability: strictNullChecks, Optional Chaining & Nullish Coalescing
Primitive Types, Arrays & Tuples
Readonly, const Assertions & Immutability
Structural Typing & Duck Typing
Template Literal Types
tsconfig & Compiler Options
Type Assertions & Type Casting
Type Inference & Contextual Typing
Type Narrowing & Guards
Types & Interfaces
Union & Intersection Types
Utility Types
Classes: Access Modifiers, Abstract Classes & Implements
10 questions found
public (the default) makes a member accessible from anywhere; private restricts access to only within the declaring class itself; protected allows access within the declaring class AND its subclasses, but not from outside code.
Real-world example
Hiding an internal PIN or password field so only the class's own methods can read it.
Never
Unknown & Void Types
The 'private' keyword is a TypeScript-only, compile-time restriction — it's erased at compile time and the field is still a regular accessible property in the emitted JavaScript. The '#' syntax is real, native JavaScript privacy enforced at runtime, invisible and inaccessible even via bracket notation or reflection.
class Account {
private tsPrivate = 1; // compile-time only, visible in emitted JS
#truePrivate = 2; // enforced at runtime, invisible outside the class
}
Real-world example
Choosing '#' private fields when you need guaranteed runtime encapsulation, such as protecting sensitive internal state from any external access.
Readonly
const Assertions & Immutability
Mark a class with the abstract keyword to prevent it from being instantiated directly with 'new' — it exists only to be extended, typically providing shared implementation plus one or more abstract methods that subclasses MUST implement.
abstract class Shape {
abstract area(): number;
describe(): string { return `Area: ${this.area()}`; }
}
class Circle extends Shape {
constructor(private radius: number) { super(); }
area() { return Math.PI * this.radius ** 2; }
}
// new Shape(); // Error: cannot instantiate an abstract class
Real-world example
Defining a base PaymentMethod class where each subclass (CreditCard, PayPal) must implement its own charge() logic.
Structural Typing & Duck Typing
implements requires a class to satisfy an interface's shape — providing all its declared members — without inheriting any implementation or behavior. extends inherits actual behavior and state from a parent class (or interface, for interface-to-interface extension), and a class can only extend one class but implement multiple interfaces.
interface Flyable { fly(): void; }
interface Swimmable { swim(): void; }
class Duck implements Flyable, Swimmable {
fly() { console.log('flying'); }
swim() { console.log('swimming'); }
}
Real-world example
Requiring a class to conform to a Serializable or Comparable-style contract without forcing a specific inheritance hierarchy.
Structural Typing & Duck Typing
Prefixing a constructor parameter with an access modifier (public, private, protected, or readonly) automatically declares a class property of that name AND assigns the parameter's value to it, removing the need to write both a field declaration and an explicit `this.x = x` assignment.
class Point {
constructor(private x: number, private y: number) {}
// equivalent to declaring 'private x: number;' and 'this.x = x;' manually
}
Real-world example
Reducing constructor boilerplate in small value classes like Point, Money, or Coordinates.
Primitive Types
Arrays & Tuples
static members belong to the class itself rather than to instances, accessed as ClassName.member. TypeScript also supports static blocks for complex static initialization logic, and access modifiers can apply to static members too (e.g. private static).
class IdGenerator {
private static nextId = 1;
static generate(): number {
return IdGenerator.nextId++;
}
}
IdGenerator.generate(); // 1
Real-world example
A shared, class-wide ID counter or a static factory method like User.fromJSON().
Modules
How does TypeScript enforce that an abstract method must be implemented, and what error occurs if it isn't?
AdvancedWhen a concrete (non-abstract) class extends an abstract class without implementing all its abstract members, the compiler raises an error like 'non-abstract class does not implement inherited abstract member', catching the omission at compile time before the code ever runs.
abstract class Shape {
abstract area(): number;
}
class Broken extends Shape {}
// Error: Non-abstract class 'Broken' does not implement inherited abstract member 'area' from class 'Shape'.
Real-world example
Guaranteeing every new payment provider or shape subtype implements its required core method, caught at compile time in CI.
Discriminated Unions & Exhaustiveness Checking
Structural typing normally only cares about shape, but private and protected members break this: two classes with identically-named private/protected members are still considered incompatible UNLESS one is literally derived from the other, because TypeScript tracks the declaring class as part of each private/protected member's identity.
class A { private secret = 1; }
class B { private secret = 1; }
let a: A = new B(); // Error: Property 'secret' is private in type 'B' but not related to A's 'secret'
Real-world example
Understanding a confusing compiler error where two seemingly identical classes aren't assignable to each other.
Structural Typing & Duck Typing
How does the 'protected constructor' pattern enforce a specific object-creation approach, like the Factory pattern?
AdvancedMaking a class's constructor protected prevents external code from calling 'new' on it directly, while still allowing subclasses (or static factory methods within the class itself) to construct instances — commonly used to force construction through a validated static factory method.
class User {
protected constructor(public readonly email: string) {}
static create(email: string): User {
if (!email.includes('@')) throw new Error('Invalid email');
return new User(email);
}
}
// new User('x'); // Error: constructor is protected
User.create('sam@example.com'); // OK
Real-world example
Guaranteeing every User instance is validated at creation time by forcing all construction through User.create().
Generics
What is the difference between an abstract class and an interface when both could technically express the same contract, and how do you decide which to use?
AdvancedAn abstract class can provide shared implementation, state, and constructor logic that subclasses inherit for free, and supports access modifiers — but a class can only extend one. An interface is purely a shape contract with zero implementation, and a class can implement many. Choose an abstract class when subclasses genuinely share behavior/state; choose an interface when you only need to enforce a shape across otherwise unrelated classes.
// Shared behavior -> abstract class
abstract class Repository<T> {
protected items: T[] = [];
abstract validate(item: T): boolean;
add(item: T) { if (this.validate(item)) this.items.push(item); }
}
// Pure contract -> interface
interface Identifiable { id: string; }
Real-world example
Choosing an abstract Repository base class with shared CRUD logic, versus a plain Identifiable interface used across many unrelated types.
Structural Typing & Duck Typing