This TypeScript error occurs when a getter and setter for the same property have incompatible return types. TypeScript requires that the getter's return type matches the setter's parameter type to ensure type safety and consistency. The error prevents runtime type mismatches by enforcing that values retrieved via getters can be safely assigned back via setters.
In TypeScript, when you define both a getter and a setter for the same property, they must have compatible types. Specifically, the type returned by the getter must be assignable to the type accepted by the setter. This ensures that any value retrieved via the getter can be safely passed back to the setter without type violations. The error occurs because TypeScript's type system cannot guarantee type safety if the getter and setter operate on different types, which could lead to runtime errors or unexpected behavior.
Look at the error message in your TypeScript compiler output or IDE. It will point to the property with mismatched getter and setter types. Examine both the getter's return type annotation and the setter's parameter type annotation to identify the discrepancy.
The simplest fix is to ensure the getter return type exactly matches the setter parameter type. If the getter returns string, the setter must accept string as its parameter.
// WRONG - mismatched types
class User {
private _age: number = 25;
get age(): string { // Returns string
return this._age.toString();
}
set age(value: number) { // Accepts number - ERROR!
this._age = value;
}
}
// CORRECT - matching types
class User {
private _age: number = 25;
get age(): string { // Returns string
return this._age.toString();
}
set age(value: string) { // Also accepts string
this._age = parseInt(value, 10);
}
}If you need to handle multiple types, use the same union type for both getter and setter. Both must accept the same set of possible types.
// WRONG - inconsistent union types
class Config {
private _value: string | number = 0;
get value(): string | number { // Union of string | number
return this._value;
}
set value(val: string) { // Only string - ERROR!
this._value = val;
}
}
// CORRECT - consistent union types
class Config {
private _value: string | number = 0;
get value(): string | number { // Union of string | number
return this._value;
}
set value(val: string | number) { // Same union type
this._value = val;
}
}If your getter and setter logically work with different types (e.g., getter returns formatted string, setter accepts raw number), you need to handle the conversion within the accessors and use a consistent public type.
// Problem: Getter returns formatted string, setter accepts number
class Product {
private _price: number = 9.99;
// Solution 1: Both use string type with internal conversion
get price(): string {
return `$${this._price.toFixed(2)}`;
}
set price(value: string) {
// Parse string back to number
this._price = parseFloat(value.replace(/[^0-9.]/g, ''));
}
// Solution 2: Use separate methods instead of accessors
getFormattedPrice(): string {
return `$${this._price.toFixed(2)}`;
}
setPrice(value: number): void {
this._price = value;
}
}TypeScript may infer different types than expected. Always explicitly annotate both getter return type and setter parameter type to avoid inference mismatches.
// WRONG - let TypeScript infer, may cause mismatch
class Example {
private _data: any;
get data() { // Inferred as any
return this._data;
}
set data(value: string) { // Explicit string - may cause mismatch
this._data = value;
}
}
// CORRECT - explicit type annotations
class Example {
private _data: string;
get data(): string { // Explicit string return type
return this._data;
}
set data(value: string) { // Explicit string parameter
this._data = value;
}
}If you only need one direction of access, consider using a readonly property (only getter) or a writeonly pattern (only setter) instead of both accessors.
// Readonly property - only getter needed
class Constants {
private readonly _PI = 3.14159;
get PI(): number { // No setter needed for constant
return this._PI;
}
}
// Writeonly pattern (less common)
class Logger {
private _lastMessage: string = '';
set lastMessage(msg: string) { // Only setter, no getter
this._lastMessage = msg;
console.log(`Logged: ${msg}`);
}
}The TypeScript requirement for getter/setter type consistency stems from the fact that accessors represent a single property from the outside world. If obj.prop returns type T when read, then obj.prop = value should accept type T when written. This maintains the principle of least surprise. However, there are advanced patterns using generic constraints or conditional types that can create more complex but still type-safe relationships between getters and setters. For example, you could use a generic class where the getter returns T and the setter accepts T | undefined. Also note that in JavaScript/TypeScript, you can have a getter without a setter (readonly) or a setter without a getter (writeonly), which avoids this error entirely. When working with inheritance, ensure that overridden accessors in derived classes maintain type compatibility with the base class accessors.
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