This error occurs when you try to assign a value to an invalid target, such as a function call result, a literal value, or an optional property access. TypeScript requires the left side of an assignment to be a valid lvalue like a variable or object property.
This TypeScript error indicates that you're attempting to assign a value to something that isn't a valid assignment target (an "lvalue" in compiler terminology). An lvalue is an expression that represents a location in memory where a value can be stored—specifically, a variable or property that can be modified. TypeScript enforces strict rules about what can appear on the left side of an assignment operator. Only the following are valid assignment targets: - A variable name (let, const, or var) - An object property (object.property) - An array element (array[index]) - A destructuring pattern (in declarations only) When you try to assign to something else—like a function's return value, a literal number or string, or an optional property access—TypeScript catches this as a syntax error and reports TS2364.
Look at the line flagged by TypeScript and identify what's on the left side of the = sign.
Common invalid patterns:
// ❌ Function call result
const result = getUser() = newValue;
// ❌ Literal value
5 = x;
'name' = value;
// ❌ Optional property access
user?.name = 'John';
// ❌ Expression result
(a + b) = value;
// ❌ Const reassignment
const name = 'John';
name = 'Jane'; // Cannot reassign const
// ❌ Assignment in non-declaration context
[a, b] = [1, 2]; // Works in declaration, not as separate statementRun TypeScript compiler to see the exact line and column number where the error occurs:
npx tsc --noEmitIf you're trying to capture a function's return value and modify it, assign it to a variable first:
// ❌ Invalid: attempting to assign to function result
getUser() = { name: 'John' };
// ✓ Correct: assign function result to a variable
const user = getUser();
user.name = 'John';
// Or modify in one step if the function returns an object
const user = { ...getUser(), name: 'John' };If you meant to call a setter function:
// ❌ This looks like assignment but isn't valid
setValue(10) = 20;
// ✓ Just call the function
setValue(20);
// Or if you need to pass a different value
const newValue = 20;
setValue(newValue);Optional chaining (?.) is designed for safe property access, not safe assignment. Use an if statement to guard the assignment:
// ❌ Invalid: optional chaining in assignment
employee?.country = 'Germany';
// ✓ Correct: guard with if statement
if (employee) {
employee.country = 'Germany';
}
// Or use nullish coalescing for default values
if (employee != null) {
employee.country = 'Germany';
}
// For simpler cases, use logical AND
employee && (employee.country = 'Germany');When updating nested optional properties:
// ❌ Invalid
user?.profile?.name = 'John';
// ✓ Correct
if (user?.profile) {
user.profile.name = 'John';
}A common mistake is using = (assignment) instead of === (comparison) inside conditionals:
// ❌ This assigns instead of comparing, and = is invalid in if condition
if (method = 'GET') {
// ...
}
// ✓ Use === for comparison
if (method === 'GET') {
// ...
}
// ✓ Use == for loose comparison (generally not recommended)
if (method == 'GET') {
// ...
}Assignment outside conditional context:
Sometimes you want to assign AND check truthiness (valid in JavaScript, but be clear):
// This is valid but confusing
if ((user = getUser())) {
// user is now assigned and truthy
}
// Better: separate concerns
const user = getUser();
if (user) {
// ...
}Destructuring assignment syntax works in declarations but not as standalone reassignments:
// ❌ Invalid: destructuring outside of declaration
[a, b] = [1, 2];
({ x, y } = point);
// ✓ Correct: use 'const', 'let', or 'var' with destructuring
const [a, b] = [1, 2];
let { x, y } = point;
// If you need to reassign existing variables with destructuring:
// Use parentheses to disambiguate from block syntax
({ x, y } = point); // ✓ Valid at statement level with parens
// Or assign to new variables
const { x: newX, y: newY } = point;If trying to reassign a const or readonly property, declare a new variable instead:
// ❌ Invalid: cannot reassign const
const name = 'John';
name = 'Jane';
// ✓ Declare a new variable
const name = 'John';
const updatedName = 'Jane';
// Or use let if reassignment is intentional
let name = 'John';
name = 'Jane'; // ✓ Valid
// For readonly properties:
interface User {
readonly id: number;
name: string;
}
const user: User = { id: 1, name: 'John' };
user.id = 2; // ❌ Error: readonly property
user.name = 'Jane'; // ✓ Valid
// Create new object with updated values
const updatedUser = { ...user, name: 'Jane' };After making fixes, verify the error is resolved:
# Check for TypeScript errors without emitting
npx tsc --noEmit
# Or run your build command
npm run build
# In VS Code, the IDE should clear the error immediatelyLook for the specific error TS2364:
npx tsc --noEmit 2>&1 | grep TS2364If the error persists:
1. Check you modified the correct line (line numbers in error message)
2. Ensure you saved the file
3. Restart your TypeScript language server in VS Code: Ctrl+Shift+P > "TypeScript: Restart TS Server"
4. Check for similar patterns elsewhere in the file
### Understanding Lvalues vs Rvalues
In compiler terminology:
- Lvalue ("left value"): An expression that refers to a memory location and can appear on the left side of assignment
- Rvalue ("right value"): An expression that produces a value but doesn't refer to a persistent memory location
let x = 5; // x is lvalue, 5 is rvalue
x = 10; // ✓ Valid: lvalue on left, rvalue on right
10 = x; // ❌ Invalid: rvalue on left### Destructuring Deep Dive
Destructuring works in declarations and in parenthesized statements:
// ✓ Declaration - always works
const [a, b] = [1, 2];
const { x, y } = point;
// ⚠ Statement-level destructuring needs parentheses
// (Otherwise { } is interpreted as a block)
({ x, y } = point);
// ❌ This is parsed as a block, not destructuring
{ x, y } = point;### Optional Chaining Limitations
Optional chaining was intentionally not designed for assignment because it creates ambiguity:
// What should happen if user is null?
user?.name = 'John'; // ❌ Invalid
// The fix is explicit:
if (user) {
user.name = 'John'; // ✓ Clear intent
}### Common TypeScript Assignment Errors
| Error | Cause | Fix |
|-------|-------|-----|
| TS2364 | Invalid lvalue | Use variable or property |
| TS2540 | Assigning to readonly property | Use spreading or new object |
| TS2588 | Cannot assign to const | Declare with let or var |
| TS1005 | Unexpected token | Check syntax of assignment |
### Safer Patterns for Object Updates
When you frequently need to update objects:
// Pattern 1: Object spreading (immutable)
const updated = { ...original, field: newValue };
// Pattern 2: Class setter methods (encapsulated)
class User {
private _name: string;
set name(value: string) {
this._name = value;
}
get name() {
return this._name;
}
}
// Pattern 3: Factory functions
function updateUser(user: User, updates: Partial<User>): User {
return { ...user, ...updates };
}
// Pattern 4: Immer library (for complex updates)
import produce from 'immer';
const updated = produce(user, draft => {
draft.profile.name = 'John';
});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