This TypeScript error occurs when you try to apply property modifiers like 'public', 'private', or 'protected' directly to a constructor declaration. Constructors in TypeScript cannot have access modifiers applied to them directly. Instead, you should use parameter properties or define properties separately in the class body.
The error "Property modifier cannot appear on a constructor declaration" happens when you attempt to use access modifiers (public, private, protected, readonly) directly on a constructor parameter or the constructor itself. In TypeScript, constructors are special methods that initialize class instances, and they follow different rules than regular methods or properties. Constructors can have parameters with access modifiers, but these create parameter properties—a shorthand syntax that automatically creates and initializes class properties. However, you cannot apply modifiers to the constructor declaration itself or to constructor parameters that aren't meant to become class properties. This error is TypeScript's way of enforcing proper class design. Constructors should focus on initialization logic, while property definitions (with their modifiers) belong in the class body or as parameter properties. This separation makes code more readable and maintainable by clearly distinguishing between property declarations and initialization logic.
If you accidentally added modifiers to the constructor itself, simply remove them:
// BEFORE: Error TS1248
class User {
public constructor(name: string) { // Error: public on constructor
// ...
}
}
// AFTER: Remove the modifier
class User {
constructor(name: string) { // OK: no modifier on constructor
// ...
}
}Constructors are always public by default and cannot have access modifiers. The only exception is private constructors for singleton patterns, which use a different syntax.
If you want constructor parameters to become class properties with modifiers, use parameter property syntax:
// BEFORE: Trying to apply modifiers incorrectly
class User {
constructor(public name: string, private age: number) { // Error: wrong syntax
// ...
}
}
// AFTER: Correct parameter property syntax
class User {
constructor(
public name: string, // OK: creates public property this.name
private age: number // OK: creates private property this.age
) {
// Properties are automatically created and initialized
}
}
const user = new User("Alice", 30);
console.log(user.name); // OK: "Alice" (public)
console.log(user.age); // Error: age is privateParameter properties automatically:
1. Declare a property with the given modifier
2. Initialize it with the parameter value
3. Make it available as this.propertyName
For more control, declare properties in the class body and assign them in the constructor:
// BEFORE: Confusing syntax
class Product {
constructor(private name: string, public price: number) {
// ...
}
}
// AFTER: Clear separation
class Product {
private name: string;
public price: number;
constructor(name: string, price: number) {
this.name = name;
this.price = price;
}
}
// Alternative with readonly
class Product {
constructor(
private readonly id: string, // OK: readonly parameter property
public name: string
) {}
}This approach is more explicit and easier to read, especially for complex classes with many properties.
For singleton patterns, use private before the constructor parameters, not on the constructor itself:
// BEFORE: Wrong singleton syntax
class Database {
private static instance: Database;
private constructor() { // Error if used incorrectly
// ...
}
static getInstance(): Database {
if (!Database.instance) {
Database.instance = new Database();
}
return Database.instance;
}
}
// AFTER: Correct private constructor
class Database {
private static instance: Database;
private constructor() { // OK: private constructor (no parameters)
// Initialization code
}
static getInstance(): Database {
if (!Database.instance) {
Database.instance = new Database();
}
return Database.instance;
}
}
// Usage
const db = Database.getInstance(); // OK
const db2 = new Database(); // Error: constructor is privatePrivate constructors prevent direct instantiation, forcing use of factory methods like getInstance().
Ensure you're not confusing regular method syntax with constructor syntax:
// BEFORE: Method syntax on constructor
class Calculator {
public constructor(public value: number = 0) { // Error: public on constructor
// ...
}
public add(x: number): void {
this.value += x;
}
}
// AFTER: Correct syntax
class Calculator {
constructor(public value: number = 0) { // OK: parameter property with default
// ...
}
public add(x: number): void {
this.value += x;
}
}
// Common confusion: constructor vs method
class Example {
// Constructor - no return type, named "constructor"
constructor(public x: number) {}
// Method - has return type, custom name
public method(y: number): void {
console.log(this.x + y);
}
}Remember: Constructors don't have return types and are always named "constructor".
Ensure your TypeScript configuration supports the syntax you're using:
// tsconfig.json - ensure proper settings
{
"compilerOptions": {
"target": "ES2022", // or later
"lib": ["ES2022", "DOM"],
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"experimentalDecorators": false, // Unless you need decorators
"emitDecoratorMetadata": false
}
}Also check your TypeScript version:
# Check TypeScript version
npx tsc --version
# Update if needed
npm install typescript@latest --save-devSome older TypeScript versions had different rules for constructor syntax. Version 4.0+ has stable parameter property syntax.
### Parameter Properties Deep Dive
Parameter properties are a TypeScript shorthand that combines parameter declaration and property initialization:
// Traditional way
class Point {
private x: number;
private y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
}
// Parameter property way (equivalent)
class Point {
constructor(
private x: number,
private y: number
) {}
}Supported modifiers on parameter properties:
- public (default if omitted in parameter properties)
- private
- protected
- readonly
- public readonly / private readonly / protected readonly
### Constructor vs Method Modifiers
| Aspect | Constructor | Regular Method |
|--------|-------------|----------------|
| Access modifiers | ❌ Cannot have public/private/protected on declaration | ✅ Can have access modifiers |
| static | ❌ Cannot be static | ✅ Can be static |
| readonly | ❌ Cannot be readonly | ❌ Not applicable (methods aren't readonly) |
| Return type | ❌ Never specified (implicitly returns instance) | ✅ Should be specified |
| Name | ✅ Always "constructor" | ✅ Any valid identifier |
### Common Pitfalls
1. Default values with parameter properties:
// Correct
class Example {
constructor(public name: string = "default") {}
}
// Incorrect (syntax error)
class Example {
constructor(public name: string = "default", public) {}
}2. Rest parameters with modifiers:
// Not allowed - rest parameters can't have modifiers
class Example {
constructor(...public args: string[]) {} // Error
}3. Destructuring parameters:
// Not allowed in parameter properties
class Example {
constructor(public {x, y}: {x: number, y: number}) {} // Error
}### TypeScript vs Other Languages
C#/Java developers note: TypeScript's constructor syntax is different:
- C#: public ClassName(...) is valid
- TypeScript: constructor(...) without public keyword
- Parameter properties (constructor(public x: number)) are TypeScript-specific
JavaScript developers note: TypeScript adds type annotations and modifiers:
- JavaScript: constructor(x) { this.x = x; }
- TypeScript: constructor(public x: number) {} or constructor(x: number) { this.x = x; }
### Best Practices
1. Use parameter properties for simple classes where properties directly map to constructor parameters.
2. Use explicit property declarations for:
- Complex initialization logic
- Properties without direct constructor parameters
- Classes with many properties (improves readability)
3. Be consistent within a codebase:
- Either use parameter properties everywhere appropriate
- Or use explicit declarations everywhere
4. Consider readability: Parameter properties are concise but can hide complexity.
### Compiler Behavior
The TypeScript compiler transforms parameter properties during compilation:
// TypeScript source
class User {
constructor(public name: string, private age: number) {}
}
// Compiled JavaScript (ES2015+)
class User {
constructor(name, age) {
this.name = name;
this.age = age;
}
}Note: The private modifier disappears in JavaScript output (compile-time only). For runtime privacy, use #privateField syntax.
Type parameter 'X' is not used in the function signature
How to fix "Type parameter not used in function signature" in TypeScript
Type parameter 'X' is defined but never used
How to fix "Type parameter is defined but never used" in TypeScript
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