microsoft / microsoft/TypeScript

Unsafe Map overload spoils direct property access on parenthesized assignment expression

Open
#61,506 2 comments 2 reactions 0 assignees View on GitHub
Domain: check: Type Inference Help Wanted Possible Improvement
Dominant language
Go
Stars
111k
Forks
14.3k
Avg merge
2d 4h
Merged PRs (30d)
132

Description

### 🔎 Search Terms

map overload any unknown unsafe weakmap set binary assignment lazy init

### 🕗 Version & Regression Information

- This is the behavior in every version I tried

### ⏯ Playground Link

https://www.typescriptlang.org/play/?ts=5.9.0-dev.20250326#code/DYUwLgBAxghlAWIBcECyMAOAeAzmATgJYB2A5gDQQD2ARgFYhRgB8EAPhAK7EAmIAZiRA8A3ACgx-bk0JViEMCDwBGABQBrEAE8UeImQCUEAN5iI0OXgj4lnYJAC8EVbAQgIAfg9PiIAO5omKoGBgB0pOAa2gbi5gD0cebmAHoeZtbgnPjyNjh2YOIAvhJSxDJyCkpgAExROhB6JKRGpuauiJ7eEL4B6BjBsRbEVrn5EE7tIOGRmlox6QlJEKnpNmBZObb2RWJAA

### 💻 Code

```ts
let cache: Map | undefined;

function test1(key: string) {
const result = (cache ??= new Map()).get(key);
// ^? const result: any
return result;
}

function test2(key: string) {
cache ??= new Map();
const result = cache.get(key);
// ^? const result: object | undefined
return result;
}
```

### 🙁 Actual behavior

First `result` gets spoiled as `any`

### 🙂 Expected behavior

I'd expect both `result`s to be `object | undefined`

### Additional information about the issue

This bug reports an issue related to `Map` but, tbf, it's not unique to `Map` and a more general solution would have to be found to properly fix this. The problem is how `checkBinaryLikeExpressionWorker` create a union of left and right types here - without taking into consideration that the assignment target might already overlap with the right type. That union type gets reduced with `UnionReduction.Subtype` and `.get` gets read from it - instead of the assigned to reference on the left side.

Contributor guide

Open the contributing guide

Research direction

Reproduce the difference between test1 and test2 using the linked TypeScript Playground, then inspect checkBinaryLikeExpressionWorker, which the report identifies as forming the union of the assignment operands. Trace how that union is reduced before .get is resolved. Done means the parenthesized assignment access infers object | undefined like the separate assignment form, with regression coverage for the reported example.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.