this.field = field assignments. TypeScript Parameter Properties compress all three steps into a single line.1. The Boilerplate Problem#
Consider a standard OOP class modeling an HTTP API Client in TypeScript without parameter properties:
// ❌ The Verbose Way (Triplicated Boilerplate)
class TraditionalApiClient {
// 1. Declare class fields
public readonly baseUrl: string;
private timeoutMs: number;
protected isDebug: boolean;
// 2. Specify constructor parameters AND perform assignment
constructor(baseUrl: string, timeoutMs: number, isDebug: boolean) {
// 3. Assign to 'this'
this.baseUrl = baseUrl;
this.timeoutMs = timeoutMs;
this.isDebug = isDebug;
}
}Notice how baseUrl, timeoutMs, and isDebug are written 3 times each! In large enterprise codebases with dependency injection (e.g., Angular or NestJS services containing 8 dependencies), constructor boilerplate quickly bloats files.
2. The Solution: Parameter Properties Syntax#
By adding an explicit access modifier (public, private, protected), readonly, or override directly in front of a constructor argument, TypeScript automatically creates the class field and assigns it for you.
// 🟢 The Concise Way (Parameter Properties)
class ConciseApiClient {
// TypeScript automatically declares fields AND assigns this.baseUrl = baseUrl, etc.
constructor(
public readonly baseUrl: string,
private timeoutMs: number,
protected isDebug: boolean
) {
// Constructor body can be completely empty!
}
public getTimeout(): number {
return this.timeoutMs;
}
}
const client = new ConciseApiClient("https://api.acme.com", 3000, false);
console.log(client.baseUrl); // "https://api.acme.com"
console.log(client.getTimeout()); // 3000
3. How Parameter Properties Compile to JavaScript#
Parameter properties are a pure TypeScript syntactic convenience. When compiled to JavaScript, tsc emits standard ES6 class property assignments:
// Compiled JavaScript Output (dist/ConciseApiClient.js)
class ConciseApiClient {
constructor(baseUrl, timeoutMs, isDebug) {
this.baseUrl = baseUrl;
this.timeoutMs = timeoutMs;
this.isDebug = isDebug;
}
getTimeout() {
return this.timeoutMs;
}
}4. Valid Parameter Property Modifiers#
To convert a constructor argument into a parameter property, you must prefix it with at least one of the following modifiers:
publicprivateprotectedreadonlypublic readonlyprivate readonlyprotected readonlyoverride(when extending base classes)
class ProductRecord {
constructor(
public id: string, // Public instance property
private secretHash: string, // Private instance property
public readonly createdAt = new Date(), // Public Readonly with Default Value!
) {}
}Omitting the Modifier: If you omit the modifier (e.g., constructor(id: string)), TypeScript treats id as a standard local constructor argument only—it will NOT create this.id on the class instance!
5. Parameter Properties in Inheritance (super())#
When extending a base class, parameter properties in the subclass are assigned after super() finishes executing.
class BaseService {
constructor(public readonly serviceName: string) {
console.log(`[BaseService] Initialized: ${serviceName}`);
}
}
class UserService extends BaseService {
constructor(
serviceName: string,
private userStoreUrl: string // Subclass Parameter Property
) {
// 1. MUST call super() first!
super(serviceName);
// 2. TypeScript automatically assigns 'this.userStoreUrl = userStoreUrl' right after super()
}
public printStore() {
console.log(`[UserService] Store URL: ${this.userStoreUrl}`);
}
}
const userSvc = new UserService("AuthService", "https://db.internal/users");
userSvc.printStore();6. Common Gotcha: Duplicate Identifier Errors#
A frequent beginner mistake is declaring the property in the class body AND adding an access modifier in the constructor:
class BadDuplicateClass {
// ❌ Declaration in class body
name: string;
// ❌ Compiler Error: Duplicate identifier 'name'.
// Property 'name' is already declared in class 'BadDuplicateClass'.
constructor(public name: string) {}
}The Rule:#
- If you use Parameter Properties (
public name: stringin constructor), do NOT declarename: stringat the top of the class.
Summary & Next Steps#
In this episode:
- We learned how Parameter Properties compress field declaration, parameter typing, and property assignment into constructor signatures.
- We listed valid modifiers:
public,private,protected,readonly, andoverride. - We traced compilation behavior into standard
this.key = keyassignments. - We analyzed inheritance behavior with
super()calls and avoided duplicate identifier errors.
In Episode 19: Getters and Setters, we will explore how to encapsulate property access control using TypeScript get and set accessors!

