16.2 Constructor Parameter Shorthand
Now, let’s talk about one of those little quality-of-life features in TypeScript that you’ll either use constantly or forget exists entirely: constructor parameter shorthand. It’s a bit of syntactic sugar, but it’s the good kind—the kind that doesn’t rot your teeth and actually saves you from carpal tunnel.
Picture this utterly mundane and repetitive scenario. You’re writing a class, and of course, you need to initialize it with some values. The old-school, verbose, Java-esque way looks like this:
class OldSchoolPerson {
name: string;
age: number;
isEmployed: boolean;
constructor(name: string, age: number, isEmployed: boolean) {
this.name = name;
this.age = age;
this.isEmployed = isEmployed;
}
}
Look at that. It’s fine. It works. But it’s also a festival of redundancy. You declare your properties. You list them in the constructor parameters. And then you perform the utterly thrilling ceremony of assigning this.thing = thing for every single one. It’s boilerplate. It’s noise. It’s 1995. We can do better.
The Shorthand Magic
TypeScript, in its infinite wisdom (or its blatant theft of ideas from C#), said, “What if we just… didn’t do that?” Behold, the constructor parameter shorthand:
class ModernPerson {
constructor(
public name: string,
public age: number,
public isEmployed: boolean
) {}
}
Yes, that’s the entire class definition. Go on, run it. I’ll wait.
See? It works exactly the same. We’ve created a class with three public properties: name, age, and isEmployed. The magic is in the parameter prefixes: public, private, protected, or readonly. When you use one of these modifiers on a constructor parameter, TypeScript automatically does two things for you:
- It declares a class property of the same name.
- It assigns the parameter’s value to that property.
It’s not runtime magic; it’s a compile-time transformation. The JavaScript it emits is the same verbose this.name = name; code you’d write by hand. TypeScript is just saving you the keystrokes. You’re welcome.
It’s Not Just for public
The beauty is this works with any access modifier. Need a private property? Easy.
class ClassWithSecret {
constructor(public id: number, private secretKey: string) {}
}
const instance = new ClassWithSecret(1, "password123");
console.log(instance.id); // Works fine, it's public
console.log(instance.secretKey); // Compiler Error: Property 'secretKey' is private and only accessible within class 'ClassWithSecret'.
This is incredibly useful for dependency injection, where you often want to inject a service, store it as a private property for the class’s internal use, and never expose it publicly.
The readonly Twist
This is where it gets really powerful. You can combine access modifiers with readonly to create immutable properties directly from the constructor.
class ImmutablePoint {
constructor(public readonly x: number, public readonly y: number) {}
}
const point = new ImmutablePoint(10, 20);
console.log(point.x); // 10
point.x = 99; // Compiler Error: Cannot assign to 'x' because it is a read-only property.
This is a fantastically clean and declarative way to create simple, immutable data structures. The readonly modifier ensures the property can only be assigned during the initial this.x = x; assignment that TypeScript generates, making it safe from mutation afterwards.
When to Avoid It (The Pitfalls)
This feature is brilliant, but it’s not a golden hammer. Don’t use it blindly.
1. When You Need Validation or Transformation. The shorthand assignment happens automatically and immediately. If you need to validate or transform an argument before assigning it to the property, you must fall back to the manual way.
class ValidatedPerson {
name: string;
age: number;
constructor(name: string, age: number) {
if (age < 0) {
throw new Error("Age cannot be negative. You're not a time traveler.");
}
this.name = name;
this.age = age; // We have to do this manually now
}
}
// Trying to use the shorthand here would assign the invalid negative age first, then you'd have to throw, which is messy.
2. When the Parameter and Property Names Diverge. The whole mechanism relies on the parameter name being the exact name you want for the property. If you need them to be different, you’re back to manual assignment.
class DatabaseEntity {
private internalId: string;
constructor(public id: number, dbId: string) {
this.internalId = `prefix-${dbId}`; // Can't use shorthand for this
}
}
3. When It Hurts Readability. For a constructor with one or two parameters, the shorthand is beautifully concise. For a constructor with, say, eight parameters, it becomes a nightmare to read.
// Please, for the love of all that is holy, don't do this:
class KitchenSink {
constructor(
public prop1: string,
public prop2: number,
private prop3: boolean,
protected prop4: any[],
public prop5: Date,
readonly prop6: string,
public prop7: Map<string, number>,
private prop8: () => void
) {}
}
This is an unreadable mess. At this point, your class is probably doing too much anyway (that’s a different chapter), but if you must have many properties, consider using a single config object parameter. It’s far more manageable.
So, the rule of thumb is this: use the shorthand for simple, direct initialization. The moment your assignment logic gets more complex than this.x = x, do it the old-fashioned way. Your future self, trying to debug at 2 AM, will thank you for the clarity.