This TypeScript error occurs when you try to provide a default value for a constructor parameter that has a visibility modifier (public, private, protected) or readonly modifier. TypeScript parameter properties cannot have default values in the constructor parameter list. The solution is to either remove the default value and initialize the property in the constructor body, or remove the visibility modifier and use a regular parameter with a default value.
In TypeScript, constructor parameter properties are a shorthand syntax that combines parameter declaration and class property initialization. When you add a visibility modifier (public, private, protected) or readonly to a constructor parameter, TypeScript automatically creates a class property with that name and visibility, and assigns the parameter value to it. However, this shorthand syntax does not support default values directly in the parameter list. This restriction exists because parameter properties are designed for simple property initialization from constructor arguments, not for setting default values. When TypeScript encounters a constructor parameter with both a visibility modifier and a default value, it raises this error to prevent confusion about when and how the property gets initialized. The error ensures that property initialization semantics remain clear and predictable.
Locate the constructor parameter that has both a visibility modifier (public, private, protected) or readonly and a default value. The error message will indicate which parameter is causing the issue.
// ERROR: Parameter 'name' of constructor gets a default value, which is not allowed
class User {
constructor(
public name: string = "Guest" // WRONG - public modifier with default value
) {}
}For parameter properties with visibility modifiers, remove the default value from the parameter list and initialize the property inside the constructor body instead.
// CORRECT - Initialize in constructor body
class User {
constructor(
public name: string // No default value here
) {
// Provide default in constructor body if needed
if (!name) {
this.name = "Guest";
}
}
}
// OR with explicit default logic
class User {
constructor(
public name: string
) {
this.name = name || "Guest";
}
}If you want a default value, make the parameter optional (using ?) and remove the visibility modifier. Then assign it to a class property manually.
// CORRECT - Optional parameter with default, manual assignment
class User {
name: string;
constructor(
name?: string // Optional parameter, no visibility modifier
) {
this.name = name || "Guest"; // Assign with default
}
}
// Alternative: Use destructuring with defaults
class User {
name: string;
constructor(options: { name?: string } = {}) {
this.name = options.name || "Guest";
}
}Declare the property separately with its default value, then assign the constructor parameter value if provided.
// CORRECT - Separate property declaration with default
class User {
public name: string = "Guest"; // Default value here
constructor(name?: string) {
if (name !== undefined) {
this.name = name; // Override default if provided
}
}
}
// For multiple properties with defaults
class Config {
public timeout: number = 5000;
public retries: number = 3;
constructor(options?: { timeout?: number; retries?: number }) {
if (options?.timeout !== undefined) this.timeout = options.timeout;
if (options?.retries !== undefined) this.retries = options.retries;
}
}For complex default initialization, consider using a factory method or static constructor pattern instead of trying to handle defaults in the constructor.
// CORRECT - Factory method pattern
class User {
private constructor(
public name: string
) {}
static create(name?: string): User {
return new User(name || "Guest");
}
}
// Usage
const user1 = User.create(); // name = "Guest"
const user2 = User.create("Alice"); // name = "Alice"
// Static constructor with options
class Config {
constructor(
public timeout: number,
public retries: number
) {}
static withDefaults(): Config {
return new Config(5000, 3);
}
static fromOptions(options: Partial<Config>): Config {
return new Config(
options.timeout ?? 5000,
options.retries ?? 3
);
}
}Reserve parameter property syntax (public/private constructor parameters) only for values that are always required and provided by the caller. Use other patterns for optional values with defaults.
// GOOD PATTERN - Parameter properties for required, separate handling for optional
class User {
public id: string; // Required - from constructor
public name: string = ""; // Optional with default
public age?: number; // Truly optional
constructor(
id: string, // Required, no modifier
name?: string,
age?: number
) {
this.id = id; // Assign required
if (name) this.name = name;
this.age = age;
}
}
// BETTER - Clear separation
class User {
constructor(
public readonly id: string, // Required, uses parameter property
public name: string = "", // ERROR - can't combine
public age?: number // ERROR - can't combine
) {}
}TypeScript's parameter property syntax is designed as syntactic sugar to reduce boilerplate when creating classes where constructor parameters map directly to class properties. The restriction against default values with parameter properties exists because the syntax public name: string = "default" in a constructor parameter would be ambiguous: does it mean "this parameter has a default value" or "create a property with this default value"? In JavaScript, constructor parameters can have default values, and class properties can be initialized with defaults, but these are separate concepts. TypeScript chooses to keep them separate for clarity. Under the hood, parameter properties are transformed during compilation: constructor(public name: string) becomes constructor(name) { this.name = name; } in the emitted JavaScript. Adding default values would complicate this transformation. Some developers find this restriction limiting, but it encourages clearer class design: required dependencies should use parameter properties, while optional configuration should use separate patterns (factory methods, builder pattern, options objects). The TypeScript team has discussed relaxing this restriction in future versions, but as of TypeScript 5.x, it remains enforced. When designing classes, consider whether a value is truly required (use parameter property) or optional with a default (use property initialization or factory pattern).
Function expression requires a return type
Function expression requires a return type
Value of type 'string | undefined' is not iterable
How to fix "Value is not iterable" in TypeScript
Type 'undefined' is not assignable to type 'string'
How to fix "Type undefined is not assignable to type string" in TypeScript
Type narrowing from typeof check produces 'never'
How to fix "Type narrowing produces never" in TypeScript
Type parameter 'T' has conflicting constraints
How to fix "Type parameter has conflicting constraints" in TypeScript