This error, TS2369: A parameter property is only allowed in a constructor parameter list., means that you’re trying to use the shorthand syntax for declaring class properties directly in the constructor’s parameter list, but you’ve accidentally applied it to a method parameter instead.
Here are the common causes and how to fix them:
-
Incorrectly applying parameter property syntax to a method: This is the most frequent culprit. You’ve likely written something like this:
class MyClass { constructor(private myValue: string) {} someMethod(public anotherValue: number) { // TS2369 error here this.anotherValue = anotherValue; } }Diagnosis: Look for
public,private,protected, orreadonlymodifiers on parameters of methods other than theconstructor.Fix: Remove the access modifier from the method parameter. The parameter will still be available within the method’s scope, and if you need to store it on the class, you’ll do so explicitly.
class MyClass { constructor(private myValue: string) {} someMethod(anotherValue: number) { // Fixed this.anotherValue = anotherValue; // Explicitly assign if needed } }Why it works: TypeScript’s parameter property syntax is a syntactic sugar specifically for the
constructorto automatically declare and initialize class members. It’s not designed to be a general-purpose shorthand for methods. -
Mistakenly copying constructor syntax to another method: Sometimes, developers might copy and paste the constructor signature and forget to adapt it for a regular method.
Diagnosis: Visually scan your class for method signatures that look suspiciously like a constructor signature, especially if they have access modifiers on their parameters.
Fix: Treat the method parameter as a regular parameter. If you intend for this parameter to be a class member, declare and assign it explicitly within the method body.
class DataProcessor { private processedData: string[] = []; constructor(initialData: string[]) { this.processedData = initialData; } // Incorrect: // processItem(public item: string) { // TS2369 // this.processedData.push(item); // } // Corrected: processItem(item: string) { // Fixed this.processedData.push(item); } }Why it works: This adheres to the standard TypeScript syntax for methods, where parameters are just values passed into the function, and class members are managed separately.
-
Accidental
readonlymodifier on a method parameter: Evenreadonlyis a parameter property modifier and is only allowed in the constructor.Diagnosis: Search for
readonlyon any parameter that isn’t in theconstructor.Fix: Remove
readonlyfrom the method parameter. If you need to ensure the parameter isn’t modified within the method, you can use a local variable or enforce immutability through other means. If you intended it to be a read-only class property, it should be declared in the constructor asreadonly.class ConfigLoader { private settings: Record<string, any>; constructor(initialConfig: Record<string, any>) { this.settings = initialConfig; } // Incorrect: // updateSetting(readonly key: string, value: any) { // TS2369 // if (this.settings[key] !== undefined) { // this.settings[key] = value; // } // } // Corrected: updateSetting(key: string, value: any) { // Fixed if (this.settings[key] !== undefined) { this.settings[key] = value; } } }Why it works: Similar to other access modifiers,
readonlyon a method parameter is not a valid construct in TypeScript. Thereadonlymodifier for parameter properties is exclusively for constructors to create immutable class members. -
Using
protectedorprivateon a method parameter: These are also parameter property modifiers.Diagnosis: Look for
privateorprotectedon parameters of any method that is not theconstructor.Fix: Remove the access modifier. These modifiers are only for constructor parameters that should become private or protected class members.
class Logger { private logEntries: string[] = []; constructor(private prefix: string) {} // Incorrect: // writeLog(private message: string) { // TS2369 // this.logEntries.push(`${this.prefix}: ${message}`); // } // Corrected: writeLog(message: string) { // Fixed this.logEntries.push(`${this.prefix}: ${message}`); } }Why it works:
privateandprotectedon method parameters are syntactically invalid because they are reserved for constructor parameter properties, which are designed to create class members. -
Typo in the method name, accidentally looking like a constructor: Less common, but if a method name is very similar to
constructor(e.g.,constuctor,contructor), and it has parameter properties, the compiler might get confused or flag it. However, the primary errorTS2369specifically points to parameter properties outside the constructor. This scenario is more about accidentally declaring a parameter property in the wrong place.Diagnosis: Review method names for any resemblance to
constructor. If a method has parameter properties and a name that could be mistaken forconstructorby a human, it’s worth checking.Fix: Rename the method to something distinct and remove any parameter property modifiers.
class Service { private serviceId: string; constructor(id: string) { this.serviceId = id; } // Imagine a typo like 'construcor' instead of 'processRequest' // processRequest(private data: any) { // TS2369 // console.log(`Processing on ${this.serviceId}:`, data); // } // Corrected (assuming 'processRequest' was intended) processRequest(data: any) { // Fixed console.log(`Processing on ${this.serviceId}:`, data); } }Why it works: Ensures method names are clear and distinct, and that parameter properties are only used where syntactically allowed (the constructor).
The next error you’ll likely encounter if you’ve fixed this and are still doing something unusual is a type mismatch if you’ve removed a parameter property without correctly assigning it to a class member elsewhere, or a TS2551: Property 'X' does not exist on type 'Y' if you try to access a property that was meant to be a parameter property but was never declared or assigned.