Parameter Properties Shorthand in TypeScript


What Are Parameter Properties
TypeScript constructors often require a familiar ritual: declare properties at the top of the class, list parameters in the constructor, then assign each parameter to this.property one by one. For a class with five properties, that's fifteen lines just to get started.
Parameter properties collapse all three steps into one. By adding an access modifier — public, private, protected, or readonly — to a constructor parameter, TypeScript automatically declares the class property and assigns the incoming value. No separate declaration, no manual assignment.
// Before: 12 lines of setup for three properties
class ProductBefore {
private name: string;
private price: number;
private inStock: boolean;
constructor(name: string, price: number, inStock: boolean) {
this.name = name;
this.price = price;
this.inStock = inStock;
}
}
// After: parameter properties shorthand
class Product {
constructor(
private name: string,
private price: number,
private inStock: boolean
) {}
}Same result, less noise. Both classes are identical at runtime.
What Makes It a Parameter Property
Adding an access modifier (public, private, protected) or readonly to
a constructor parameter tells TypeScript to: 1) declare a class property
with that name and type, and 2) assign the constructor argument to it
automatically. Without a modifier, the parameter is just a regular local
variable.
The Shorthand Syntax
The rule is simple: any constructor parameter prefixed with public, private, protected, or readonly becomes a parameter property — declared and initialized in one step.
class Point {
constructor(
public x: number,
public y: number
) {}
}
const origin = new Point(0, 0);
console.log(origin.x); // 0
console.log(origin.y); // 0The empty constructor body {} is intentional — TypeScript handles the property assignment implicitly. You only add code there when you need additional initialization logic beyond simple assignment.
Access Modifiers as Parameters
All four access modifiers work as parameter properties, each behaving exactly as it would on a regular class property:
class BankAccount {
constructor(
public readonly id: string, // accessible everywhere, immutable after construction
public owner: string, // accessible from anywhere
protected balance: number, // accessible in this class and subclasses
private pin: string // accessible only within this class
) {}
deposit(amount: number): void {
this.balance += amount;
}
validatePin(input: string): boolean {
return this.pin === input;
}
}
const account = new BankAccount("ACC-001", "Alice", 1000, "1234");
console.log(account.id); // "ACC-001"
console.log(account.owner); // "Alice"
// account.balance // Error: property 'balance' is protected
// account.pin // Error: property 'pin' is privateCombine readonly with any modifier
public readonly is one of the most useful combinations. It exposes a
property for external reading while preventing accidental reassignment after
the object is constructed — ideal for IDs, configuration values, and injected
dependencies.
Readonly Parameter Properties
readonly can stand alone (defaulting to public) or combine with private or protected. It prevents reassignment after construction:
class AppConfig {
constructor(
readonly apiUrl: string, // public readonly (implicit)
private readonly apiKey: string, // private readonly
readonly timeout: number = 30000 // with a default value
) {}
getHeaders(): Record<string, string> {
return { Authorization: `Bearer ${this.apiKey}` };
}
}
const config = new AppConfig("https://api.example.com", "secret-key");
console.log(config.apiUrl); // "https://api.example.com"
// config.apiUrl = "other"; // Error: cannot assign to 'apiUrl' because it is a read-only propertyreadonly Does Not Mean Deeply Immutable
readonly prevents reassigning the property reference, but if the value is an object or array, its contents can still be mutated. For deep immutability, use Readonly<T> or as const on the value itself.
Mixing Regular and Parameter Properties
Not every constructor parameter needs to become a class property. You can freely mix regular parameters (no modifier) with parameter properties:
class Rectangle {
area: number;
constructor(
public width: number, // becomes a property
public height: number, // becomes a property
label: string // regular parameter — NOT stored on the class
) {
this.area = width * height;
console.log(`Created ${label} rectangle`);
}
}
const rect = new Rectangle(10, 20, "landscape");
console.log(rect.width); // 10
console.log(rect.height); // 20
console.log(rect.area); // 200
// rect.label // Error: property does not existlabel is used only inside the constructor and discarded afterward — it never becomes a class property. This pattern is useful when you need a parameter for initialization logic but don't need to store it.
Real-World Patterns
Parameter properties are especially effective in service classes that receive dependencies through their constructor:
class UserService {
constructor(
private readonly db: Database,
private readonly cache: CacheClient,
private readonly logger: Logger,
private readonly config: AppConfig
) {}
async findUser(id: string): Promise<User | null> {
const cached = await this.cache.get(`user:${id}`);
if (cached) return cached;
const user = await this.db.query<User>(
`SELECT * FROM users WHERE id = $1`,
[id]
);
if (user) {
await this.cache.set(`user:${id}`, user, this.config.cacheTtl);
this.logger.info(`User ${id} fetched from database`);
}
return user;
}
}Four dependencies. Four lines. No separate property declarations, no this.x = x assignments. Dependency injection patterns like this are where parameter properties save the most boilerplate.
Best Practices
Use parameter properties when:
- A constructor parameter maps directly to a stored property (1-to-1 relationship)
- You want to reduce boilerplate in simple data or value classes
- Injecting dependencies into a service, repository, or controller
Use explicit property declarations when:
- The stored value is transformed from the incoming parameter
- Complex initialization logic is needed before assignment
- The class has many properties and explicit declarations improve readability
// Good candidate for shorthand — direct assignment
class Coordinates {
constructor(
public readonly lat: number,
public readonly lng: number
) {}
}
// Better with an explicit property — value is transformed before storing
class Temperature {
private readonly kelvin: number;
constructor(celsius: number) {
this.kelvin = celsius + 273.15; // transformation, not direct assignment
}
toCelsius(): number {
return this.kelvin - 273.15;
}
}Many teams adopt a simple convention: use parameter properties for dependency injection and plain data classes, explicit properties everywhere else. Consistency within a codebase matters more than the specific choice.
Key Takeaways
- Add
public,private,protected, orreadonlyto a constructor parameter to make it a parameter property - TypeScript declares the class property and assigns the constructor argument automatically — no
this.x = xneeded - All four modifiers work, and
readonlycan combine with any of them - Regular parameters (no modifier) and parameter properties can be freely mixed in the same constructor
- Best suited for direct 1-to-1 assignments — use explicit properties when you need to transform the value or run complex initialization logic
Phase 3 Complete
You've finished the Classes and OOP phase of this series. You now know TypeScript classes from the ground up: properties, methods, access modifiers, getters and setters, static members, abstract classes, inheritance, interface implementation, and parameter properties shorthand. Up next: Union Types and the start of TypeScript's advanced type system.