Repository: TypeScript
Issue: Intersection of merged declarations - function types behave inconsistently
Labels: Suggestion,In Discussion

### 🔎 Search Terms

declaration merging

### 🕗 Version & Regression Information

- This is a type inference problem
- This problem appears in Typescript 3.3.3, 3.5.1, 3.9.7, 4.9.5, 5.3.3 and 5.4.0-dev.20240207
- This is the behavior in every version I tried, and I reviewed the FAQ for entries about declaration merging


### ⏯ Playground Link

https://www.typescriptlang.org/play?#code/C4TwDgpgBAsiCCAnRBDEBRAHsCA7AzgJYD2uAPACoB8UAvFEqiJTQGRQDeU+AFgK4AzAQBsIACgCUALigA3YoQAmUAL4AoNYVw5EAlAGNoAMWLFOUAOYRgAGUL5gkmYzRkHiLRZrqtOvYagTMy4rW3tHaVgEZDQsHAIScndPb01tCF0DaAAhFERzULsHJwYY5mTcL1U0vyyoXPyQ6yKImTgXDGw8IlI3YA9K1LVQSEDTFopwaHoAJWs+RFxJyDIggG0AIkLwjYBdGihDo4B6Y6gUfCgITEh9HEUZCos13ah2Dl5BEXFI+SUAbnUIxyeQmUzoUDmwAWSymZAam22Dj2ByOh1O50u11u90e-U8LzenE+QlEJT+ikBw3BRlB4WW00h80WDLIYiCAB8GhJEc0dvsoBiLlcbhA7hAHtx8ZVCe8Sd9yQpKeo1KJgFABKYjLgIWIOCpMWNiBIAHRI4D-NFW61WjFcEodPoDLyWkrtMpxbqJJ0pS3qNVQABGeW1uv1hu5Zr5DktNrjaLtUAdZR9g1dkXdTE9CV6TyoftV1g1IZ19D1BuFnMj5tj8dtZ3tkUdefTbWiWa6OaS0pdqigHPMyaYqd7bvbsU7PW7zvz1QDekQLRktMX9PB9AX2sktbrCbOvGIfGEygA7sREABrAA0Qb46oyiHPUjUxwAVFA1AyoAByJ4vb9QPYUC4MQ6oXEQFi4CggaiFAwBmMCP6ZhO8RTiOVDfiaaiHAACo+kCIKAP7yqIAFAQAtvYRCVIBOqIb+Pb-re6qIBAACOfCEKxyhaHB4LflwJE-DIFJ+phYgAEwAMwSRJEhqK+xwaOWhpBKa5qSCaQnbru8YYmel74GoKnCtW0YRFp-Ckj8O66XuUAGReRkmZcVZ5Op5madpEi2buGLoMgT5QHhxAEURv5Wd8AGKMQECXCB96YOEUCkHxowMc6-4mpJUlSQAnBIQA

### 💻 Code

```ts
type MyArrayExtension<T> = Array<T> & { shuffle(): void }

interface Foo { getList(): Array<string> }
interface Foo { getList(): MyArrayExtension<string> }
interface Bar { getList(): Array<string> }
interface Bar { getList(): MyArrayExtension<string> }

type FooListType = ReturnType<Foo["getList"]>       // as expected: string[] & {shuffle(): void;}
type BarListType = ReturnType<Bar["getList"]>       // as expected: string[] & {shuffle(): void;}
type FarListType = ReturnType<(Foo|Bar)["getList"]> // as expected: string[] & {shuffle(): void;}

let fooFn = ({} as Foo).getList;                    // { (): Array<string>; (): MyArrayExtension<string>; }
let barFn = ({} as Bar).getList;                    // { (): Array<string>; (): MyArrayExtension<string>; }
let farFn = ({} as Foo|Bar).getList;                // { (): Array<string>; (): MyArrayExtension<string>; } | { (): Array<string>; (): MyArrayExtension<string>; }
let farList: FarListType = farFn();                 // should work, but error:
/* 
Type 'string[]' is not assignable to type 'MyArrayExtension<string>'.
  Property 'shuffle' is missing in type 'string[]' but required in type '{ shuffle(): void; }'.(2322)
*/

({} as Foo).getList().shuffle();                    // works
({} as Bar).getList().shuffle();                    // works
({} as Foo|Bar).getList().shuffle();                // Error: Property 'shuffle' does not exist on type 'string[]'.(2339)
```


### 🙁 Actual behavior

There are two interfaces (`Foo` and `Bar`), that each are constituted of two merged declarations.
Each interface individually behaves such that the later declaration wins (`getList(): MyArrayExtension<string>` in either case). When changing the order (define the `MyArrayExtension` returning interfaces first), the return type accordingly is `string[]`.
However, when intersecting `Foo|Bar`, the behaviour becomes inconsistent:
* `ReturnType<(Foo|Bar)["getList"]>` is what I would expect (compatible with `MyArrayExtension<string>`), but
* `({} as Foo|Bar).getList()` is not assignable to a variable of this type

### 🙂 Expected behavior

My use case is the following: I have an external dependency that defines interfaces with functions that return `T[]`.
Since I know those functions return a specific type that extends `T[]`, I want to augment the module (using `declare module`) and give the functions a more specific return type.
The behaviour for non-intersected types if perfect for this. The later (e.g. provided by my `declare module` augmentation) wins.
However, it breaks down when intersecting two types from the external dependency, as described above.

I would expect type inference to work consistently. I think it's good to be able to override/make types more specific this way, and I think this ability should not be lost when intersecting types for which this has been done.

### Additional information about the issue

@jcalz explained to me on [StackOverflow](https://stackoverflow.com/questions/77946850/intersection-of-augmented-types-infered-as-intersection-of-original-types) that this problem is not restricted to module augmentation, but appears even when merging declarations in the same file. The problem is probably related to #19064.