Skip to content
TypeScript ts classes 3 min read

Parameter Properties

Parameter properties are a TypeScript shorthand that lets you declare and initialize a class field directly from a constructor parameter. By adding an access modifier (public, private, protected) or readonly in front of a constructor parameter, you tell the compiler to create a field of the same name and assign the argument to it automatically. This removes the repetitive “declare, accept, assign” boilerplate that plain classes require. It is a TypeScript-only feature with no JavaScript equivalent, and it is especially common in dependency-injection frameworks like Angular and NestJS. This page covers the syntax, what it expands to, modifier combinations, and the rules and trade-offs.

The problem they solve

Without parameter properties you write the same field name three times — once to declare it, once as a parameter, and once to assign it. This is pure boilerplate.

class User {
  private id: string;
  public name: string;

  constructor(id: string, name: string) {
    this.id = id;
    this.name = name;
  }
}

Every field repeats: a declaration, a parameter, and an assignment line. For a class with several dependencies this triples in size for no added meaning.

The shorthand syntax

Add a modifier to the constructor parameter and TypeScript declares the field and assigns it for you. The class below is equivalent to the one above.

class User {
  constructor(
    private id: string,
    public name: string,
  ) {}
}

const u = new User("u1", "Ada");
u.name; // ✅ "Ada" — field created automatically
u.id;   // ❌ Error: Property 'id' is private

The modifier is what triggers the behavior. private id becomes a private field; public name becomes a public field. The constructor body is empty because the assignments are generated.

What it compiles to

It helps to see the emitted JavaScript. The modifiers are erased (they are types), but the assignments remain — proving these are real fields at runtime.

// TypeScript
class Point {
  constructor(public x: number, public y: number) {}
}
// Emitted JavaScript
class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
}

The compiler injects this.x = x and this.y = y at the top of the constructor. This is why a parameter property is indistinguishable from a manually declared field once compiled.

Combining with readonly

You can mix readonly with an access modifier, or use readonly alone (which implies public). This is the idiomatic way to declare immutable dependencies.

class HttpService {
  constructor(
    public readonly baseUrl: string,
    private readonly timeout: number = 5000,
  ) {}

  describe(): string {
    return `${this.baseUrl} (${this.timeout}ms)`;
  }
}

const s = new HttpService("https://api.dev");
s.baseUrl = "x"; // ❌ Error: Cannot assign to 'baseUrl' (read-only)

Note that timeout also has a default value — parameter properties support defaults and optionality just like normal parameters. readonly alone still creates a public, immutable field.

Modifier reference

Exactly one of the following must be present for the parameter to become a property. A bare parameter (no modifier) stays an ordinary local argument.

DeclarationResulting field
public x: TPublic field x
private x: TPrivate field x
protected x: TProtected field x
readonly x: TPublic, read-only field x
private readonly x: TPrivate, read-only field x
x: T (no modifier)No field — plain parameter
class Mixed {
  field?: string;

  constructor(
    public id: string, // parameter property
    plain: string,     // plain parameter — NOT a field
  ) {
    this.field = plain; // assign manually if you need it
  }
}

Parameter properties only work in a real constructor. They cannot be used with #private fields — the ECMAScript #name syntax is not allowed in parameter position, so use a TypeScript modifier instead.

Best Practices

  • Use parameter properties to eliminate constructor boilerplate, especially for injected dependencies.
  • Mark injected services private readonly so they cannot be reassigned.
  • Keep the parameter list readable; put each property on its own line with a trailing comma.
  • Remember a parameter without a modifier is just a local — add one to make it a field.
  • Mix in defaults and optional markers (= value, ?) when appropriate.
  • Avoid combining many parameter properties with heavy constructor logic — it hurts readability.
  • Reach for a TypeScript modifier rather than #private, which cannot appear in parameters.
Last updated June 29, 2026
Was this helpful?