0
0
Typescriptprogramming~15 mins

Getter and setter with types in Typescript - Deep Dive

Choose your learning style9 modes available
Overview - Getter and setter with types
What is it?
Getters and setters are special methods in TypeScript classes that let you read or change the value of a property while controlling how it happens. A getter lets you get a property value like a normal variable, but it runs code behind the scenes. A setter lets you set a property value, also running code to check or change the input. Using types with getters and setters means you tell TypeScript what kind of data these properties hold, helping catch mistakes early.
Why it matters
Without getters and setters, you might directly access or change data in ways that cause bugs or inconsistent states. They help protect your data by controlling how it is read or changed. Adding types makes your code safer and clearer, so you avoid errors like putting a number where a string should be. This leads to more reliable programs and easier maintenance.
Where it fits
Before learning getters and setters with types, you should understand basic TypeScript classes, properties, and type annotations. After this, you can explore advanced class features like access modifiers, readonly properties, and decorators to further control class behavior.
Mental Model
Core Idea
Getters and setters with types let you safely control how class properties are read and changed by wrapping access in typed methods that look like normal property use.
Think of it like...
It's like having a mailbox with a lock: the getter is the slot where you can see or take mail safely, and the setter is the locked door where you put mail in, but only if it fits the right size and shape.
Class Example
┌─────────────────────────────┐
│        MyClass              │
│ ┌───────────────┐          │
│ │ _property     │  (private)│
│ └───────────────┘          │
│ ┌───────────────┐          │
│ │ get property()│  ← getter │
│ └───────────────┘          │
│ ┌───────────────┐          │
│ │ set property()│  ← setter │
│ └───────────────┘          │
└─────────────────────────────┘
Build-Up - 6 Steps
1
FoundationBasic class properties and types
🤔
Concept: Introduce how to declare class properties with types in TypeScript.
class Person { name: string; age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } } const p = new Person('Alice', 30); console.log(p.name); // Alice console.log(p.age); // 30
Result
The class Person has two typed properties: name (string) and age (number). Creating an instance and accessing these properties works as expected.
Understanding how to declare typed properties is the foundation for controlling access with getters and setters.
2
FoundationIntroducing getters and setters syntax
🤔
Concept: Show how to define getter and setter methods in a class to control property access.
class Person { private _name: string; constructor(name: string) { this._name = name; } get name(): string { return this._name; } set name(newName: string) { this._name = newName; } } const p = new Person('Bob'); console.log(p.name); // Bob p.name = 'Charlie'; console.log(p.name); // Charlie
Result
The getter and setter allow reading and updating the private _name property using p.name syntax.
Getters and setters let you control access to private data while keeping a simple property-like interface.
3
IntermediateAdding type safety to getters and setters
🤔Before reading on: do you think TypeScript enforces types on both getter return and setter parameter? Commit to your answer.
Concept: Explain how TypeScript uses types on getter return values and setter parameters to catch errors.
class Person { private _age: number; constructor(age: number) { this._age = age; } get age(): number { return this._age; } set age(newAge: number) { if (newAge < 0) { throw new Error('Age cannot be negative'); } this._age = newAge; } } const p = new Person(25); console.log(p.age); // 25 p.age = 30; // OK // p.age = 'old'; // Error: Type 'string' is not assignable to type 'number'
Result
TypeScript enforces that the getter returns a number and the setter accepts only a number, preventing wrong types.
Knowing that types apply to both getter output and setter input helps prevent bugs and misuse of properties.
4
IntermediateUsing getters and setters for computed properties
🤔Before reading on: do you think getters can compute values dynamically each time they are accessed? Commit to your answer.
Concept: Show how getters can calculate values on the fly instead of just returning stored data.
class Rectangle { constructor(private width: number, private height: number) {} get area(): number { return this.width * this.height; } set area(value: number) { throw new Error('Area is read-only'); } } const rect = new Rectangle(5, 10); console.log(rect.area); // 50 // rect.area = 60; // Error: Area is read-only
Result
The getter calculates area dynamically; the setter throws an error to prevent changes.
Understanding that getters can compute values allows you to create read-only or derived properties.
5
AdvancedCombining private fields with typed getters/setters
🤔Before reading on: do you think private fields (#field) work seamlessly with getters and setters? Commit to your answer.
Concept: Teach how to use modern private fields with getters and setters for better encapsulation and type safety.
class BankAccount { #balance: number; constructor(initialBalance: number) { this.#balance = initialBalance; } get balance(): number { return this.#balance; } set balance(amount: number) { if (amount < 0) { throw new Error('Balance cannot be negative'); } this.#balance = amount; } } const account = new BankAccount(1000); console.log(account.balance); // 1000 account.balance = 1200; // OK // account.#balance = 500; // Error: Private field
Result
Private field #balance is safely accessed and modified only through typed getter and setter.
Knowing how private fields integrate with getters/setters improves data hiding and prevents accidental external changes.
6
ExpertTypeScript type inference and getter/setter compatibility
🤔Before reading on: do you think getter and setter types must exactly match, or can they differ? Commit to your answer.
Concept: Explore how TypeScript infers types for getters and setters and the rules for their compatibility.
class Example { private _value: number | string = 0; get value(): number { return typeof this._value === 'number' ? this._value : 0; } set value(val: number | string) { this._value = val; } } const ex = new Example(); ex.value = 'hello'; console.log(ex.value); // 0 (getter returns number) ex.value = 42; console.log(ex.value); // 42
Result
Getter returns only number, setter accepts number or string; TypeScript enforces getter return type but setter can accept wider types.
Understanding getter/setter type compatibility helps avoid subtle bugs and design flexible APIs.
Under the Hood
At runtime, getters and setters are special methods linked to a property name on the class prototype. When you access the property, the getter method runs and returns a value. When you assign to the property, the setter method runs with the new value. TypeScript uses static analysis to check types during development but compiles to JavaScript where these methods behave like normal functions. Private fields (with #) are enforced by JavaScript engines to be inaccessible outside the class, ensuring true encapsulation.
Why designed this way?
Getters and setters were designed to provide controlled access to object properties without changing how code looks when using them. This keeps code clean and readable while allowing validation, computed values, or side effects. TypeScript adds static typing to catch errors early, improving developer confidence. Private fields were introduced later to fix the problem that 'private' in TypeScript was only compile-time and not enforced at runtime.
Access flow:

Caller code
   │
   ▼
Property access (obj.prop)
   │
   ├─ If getter exists → call getter method → return value
   │
   └─ Else → return stored property value

Property assignment (obj.prop = val)
   │
   ├─ If setter exists → call setter method with val
   │
   └─ Else → set stored property value

TypeScript checks types on getter return and setter parameter at compile time.

Private field (#field) is stored in a hidden slot inaccessible outside class.
Myth Busters - 4 Common Misconceptions
Quick: Does a setter always have to accept the same type as the getter returns? Commit to yes or no.
Common Belief:The setter must accept exactly the same type that the getter returns.
Tap to reveal reality
Reality:The setter can accept a wider or different type than the getter returns, but the getter's return type must be compatible with the declared property type.
Why it matters:Assuming setter and getter types must match exactly can limit design flexibility and cause confusion when building APIs that accept multiple input types but return a normalized output.
Quick: Can you access private fields directly from outside the class? Commit to yes or no.
Common Belief:Private fields declared with # can be accessed or modified from outside the class if you use getters and setters.
Tap to reveal reality
Reality:Private fields with # are truly private and cannot be accessed or modified directly from outside the class, even with getters and setters; only the class methods can access them.
Why it matters:Misunderstanding this can lead to attempts to bypass encapsulation, causing runtime errors or security issues.
Quick: Are getters and setters just syntactic sugar for normal methods? Commit to yes or no.
Common Belief:Getters and setters are just normal methods with special names and no real difference in behavior.
Tap to reveal reality
Reality:Getters and setters behave like properties when accessed or assigned, allowing property-like syntax while running code, which is different from calling methods explicitly.
Why it matters:Confusing them with normal methods can lead to incorrect usage or misunderstanding of how property access triggers code execution.
Quick: Does TypeScript enforce types of getters and setters at runtime? Commit to yes or no.
Common Belief:TypeScript enforces getter and setter types at runtime to prevent wrong data.
Tap to reveal reality
Reality:TypeScript only checks types at compile time; at runtime, JavaScript runs without type checks, so runtime errors can still occur if code is incorrect.
Why it matters:Relying solely on TypeScript for runtime safety can cause bugs; additional runtime checks or tests are needed for full safety.
Expert Zone
1
Getters and setters can be used to create virtual properties that do not store data but compute or transform it dynamically, enabling powerful abstractions.
2
TypeScript's structural typing means that objects with compatible getter/setter signatures can be assigned interchangeably, which can lead to subtle bugs if not carefully designed.
3
Using private fields (#) with getters/setters ensures true encapsulation at runtime, unlike TypeScript's private keyword which is only compile-time enforced.
When NOT to use
Avoid getters and setters when simple public properties suffice without validation or computation, as they add overhead and complexity. For complex state management or side effects, consider using methods or reactive frameworks instead. Also, do not use getters/setters for asynchronous operations since they must return immediately.
Production Patterns
In real-world TypeScript projects, getters and setters are used to enforce validation rules, lazy-load computed values, or trigger side effects like logging. Private fields combined with getters/setters protect sensitive data. Libraries often use them to create clean APIs that hide internal complexity while maintaining type safety.
Connections
Encapsulation in Object-Oriented Programming
Getters and setters implement encapsulation by controlling access to object data.
Understanding getters and setters deepens your grasp of encapsulation, a core OOP principle that protects object integrity.
Property Accessors in C#
TypeScript getters and setters are similar to C# property accessors, both providing controlled property access with type safety.
Knowing how other languages implement property accessors helps appreciate TypeScript's design and cross-language patterns.
Security Checks in Physical Locks
Getters and setters act like security checks controlling who can read or write data, similar to locks controlling physical access.
Recognizing this connection highlights the importance of controlled access in both software and real-world security.
Common Pitfalls
#1Setting a property with the wrong type causes runtime errors.
Wrong approach:class Person { private _age: number = 0; get age(): number { return this._age; } set age(value: number) { this._age = value; } } const p = new Person(); p.age = 'old'; // No compile error if type is any or missing, causes runtime issues
Correct approach:class Person { private _age: number = 0; get age(): number { return this._age; } set age(value: number) { this._age = value; } } const p = new Person(); p.age = 30; // Correct type assignment
Root cause:Not specifying or enforcing types on setter parameters allows wrong types to be assigned, leading to bugs.
#2Trying to access private fields directly from outside the class.
Wrong approach:class Account { #balance = 100; } const acc = new Account(); console.log(acc.#balance); // Syntax error
Correct approach:class Account { #balance = 100; get balance() { return this.#balance; } } const acc = new Account(); console.log(acc.balance); // 100
Root cause:Misunderstanding that private fields are truly private and require access through class methods.
#3Defining only a setter without a getter causes unexpected behavior.
Wrong approach:class Example { set value(val: number) { console.log('Set', val); } } const ex = new Example(); ex.value = 10; console.log(ex.value); // undefined, no getter defined
Correct approach:class Example { private _val = 0; get value(): number { return this._val; } set value(val: number) { this._val = val; } } const ex = new Example(); ex.value = 10; console.log(ex.value); // 10
Root cause:Forgetting to define a getter means reading the property returns undefined, confusing users.
Key Takeaways
Getters and setters let you control how class properties are read and written while keeping simple property syntax.
TypeScript enforces types on getter return values and setter parameters to catch errors early and improve code safety.
Getters can compute values dynamically, and setters can validate or transform input before storing it.
Private fields combined with getters and setters provide true encapsulation and protect internal data.
Understanding the runtime behavior and TypeScript's static checks helps avoid common mistakes and design robust APIs.