microsoft / microsoft/TypeScript

Regression: Generic function returning union of tuples is not assignable to identical type since v4.2

Open
#63,019 5 comments 0 reactions 0 assignees View on GitHub
Domain: check: Type Inference Help Wanted Possible Improvement
Dominant language
Go
Stars
111k
Forks
14.3k
PR merge metrics
PR metrics pending

Description

### 🔎 Search Terms

generic function assignability, union return type, tuple union, TS2322, regression v4.2, generic type alias

### 🕗 Version & Regression Information

- This changed in commit [34f0e32 Bump version to 4.2.3 and LKG](https://github.com/microsoft/TypeScript/commit/34f0e32dc667eb8c7b52b3fc4067316b4ae97774)
- This also reproduces on Nightly version

### ⏯ Playground Link

https://www.typescriptlang.org/play/?#code/C4TwDgpgBAShCGATAglAvFAPDAfACgEp0coBtAM3gBsBnCAGlgF0oAfM4AJwFcHY2o3AHaII5AJZCIiJgG4AsAChQkWAkQAhdFlyFiZSrT4wW7Ul16MYA4aIlSZCxUoD0LqAFFOnAPacAXFAAKuDQAORwSMhhUOI0UEI+wFDwNDTiAOZC8ABGVNDAPlAq4ZGaYQB0SgDGPkI0ycAQDdp4APqBZcgEnepaaCRtTq7uyLRFABbwYJD1UADu4sATUD45AFYQ1Y2hNEolakgA8huoGNj4RANQAN5QiHUQgYZ0slAAbtS8nVAAvgJ3B5SQIWCBvT5Ub78di2MSSaR-JwHMondb9HSXfSAx7PaivD5fJ78f7sbHA4o8MEEyFE6wwkRwhyIpQjTzePyBEKqCLqVHRWLxRLJVLpLK5fLFIoHHnHDYaSo1OotJoNVGtDqHRB8nqa1H9QayIA

### 💻 Code

```ts
type ReadA = () => [false, R] | [true, R | undefined];
type ReadB = () => [false, R] | [true, R | undefined];

// Error: Type 'ReadA' is not assignable to type 'ReadB'.
const test = (_: ReadA): ReadB => _;

// Also happens with object types
type ReadObjA = () => { done: false; value: R } | { done: true; value: R | undefined };
type ReadObjB = () => { done: false; value: R } | { done: true; value: R | undefined };

// Error: Type 'ReadObjA' is not assignable to type 'ReadObjB'.
const testObj = (_: ReadObjA): ReadObjB => _;
```

### 🙁 Actual behavior

The code fails to compile with error TS2322: Type 'ReadA' is not assignable to type 'ReadB'.

It appears that the compiler incorrectly widens the generic type parameter `R` in the source type.
Although `ReadA` is defined as `[false, R] | ...`, the error message reports the source type as having `[false, R | undefined]`.

It seems that during the generic signature assignability check, the undefined from the `[true, R | undefined]` branch is leaking into the inference of `R` for the entire union, causing the `false` branch to become `[false, R | undefined]`. This widened source type is then incompatible with the target's more strict `[false, R]` branch.

```
Type 'ReadA' is not assignable to type 'ReadB'.
Type '[true, R | undefined] | [false, R | undefined]' is not assignable to type '[false, R] | [true, R | undefined]'.
Type '[false, R | undefined]' is not assignable to type '[false, R] | [true, R | undefined]'.
Type '[false, R | undefined]' is not assignable to type '[false, R]'.
Type at position 1 in source is not compatible with type at position 1 in target.
Type 'R | undefined' is not assignable to type 'R'.
'R' could be instantiated with an arbitrary type which could be unrelated to 'R | undefined'.(2322)
```

```
Type 'ReadObjA' is not assignable to type 'ReadObjB'.
Type '{ done: false; value: R | undefined; } | { done: true; value: R | undefined; }' is not assignable to type '{ done: false; value: R; } | { done: true; value: R | undefined; }'.
Type '{ done: false; value: R | undefined; }' is not assignable to type '{ done: false; value: R; } | { done: true; value: R | undefined; }'.
Type '{ done: false; value: R | undefined; }' is not assignable to type '{ done: false; value: R; }'.
Types of property 'value' are incompatible.
Type 'R | undefined' is not assignable to type 'R'.
'R' could be instantiated with an arbitrary type which could be unrelated to 'R | undefined'.(2322)
```

### 🙂 Expected behavior

The code should compile without errors because `ReadA` and `ReadB` are structurally identical types.

### Additional information about the issue

_No response_

Contributor guide

Open the contributing guide

Research direction

Start with the linked TypeScript Playground reproduction and compare the tuple and object examples against the regression information for v4.2. Trace the generic signature assignability check described in the issue, then validate that both structurally identical pairs compile without TS2322.

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
Active
Clarity
Mostly clear
Newbie friendliness
50/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.