Repository: TypeScript
Issue: Modify Parameters Utility in case of union/intersection of function types
Labels: Suggestion,Awaiting More Feedback

### 🔍 Search Terms

Parameters Utility with union of functions
Parameters Utility with intersection of functions


### ✅ Viability Checklist

- [X] This wouldn't be a breaking change in existing TypeScript/JavaScript code
- [X] This wouldn't change the runtime behavior of existing JavaScript code
- [X] This could be implemented without emitting different JS based on the types of the expressions
- [X] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
- [X] This isn't a request to add a new utility type: https://github.com/microsoft/TypeScript/wiki/No-New-Utility-Types
- [X] This feature would agree with the rest of our Design Goals: https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals

### ⭐ Suggestion

This proposal suggests revising the `Parameters` utility type.

There are multiple sub-proposals which may be considered independently.  The sub-proposals are:

#### Case 1:  The function is a union of function types

```ts
// Case 1:
type POr = Parameters< ((x:1|2)=>void) | ((x:2|3)=>void) >;
// Current: [x: 1 | 2] | [x: 2 | 3]
// Proposed: [x: 1 | 2] & [x: 2 | 3]
//  with reduction -> [x: 2]
```
There is no way to call the function with a value of type `1` or `3` without risking a runtime error.  The only safe value is `2`.  The proposed type reflects this.  This agrees with existing TypeScript inference behavior.
```ts
declare const f:((x:1|2)=>void) | ((x:2|3)=>void);
f(1); // error;
```
Therefore the proposed definition is:
```
Parameters<T|U> = Parameters<T> & Parameters<U>
```
Because there is no way to generate an `&` type using a distributed conditional generic type (i.e., the way it currently written, `Parameters` would have to become an intrinsic type function.


#### Case 2.x The function type is an intersection or overload of function types

In these cases also `Parameters` would have to become an intrinsic type function.

#### Case 2.1 The function type is an intersection of function types

```ts
// Case 2.1
type PAnd = Parameters< ((x:1|2)=>void) & ((x:2|3)=>void) >;
// PAnd = [x: 2 | 3]
// Proposed: [x: 1 | 2] | [x: 2 | 3]
```
Currently TypeScript treats an intersection like an overload list, and then picks the last overload under the assumption that it is the "most permissive catch-all case".  This assumption is not always true.  When it is not true, `Parameters` is not useful.

The proposed type could be used as the parameter signature for the implementation of the function intersection.


#### Case 2.2.1 The function type is an overload type without the catch-all case

```ts
// Case 2.2.1
type POverloadNoCover2 = Parameters< {
    (x:1|2):void;
    (x:2|3):void;
}>;
// POverloadNoCover2 = [x: 2 | 3]
// Proposed: [x: 1 | 2] | [x: 2 | 3]
```
Therefore the proposed definition is:
```
Parameters<T&U> = Parameters<T> | Parameters<U>
```

Currently TypeScript picks the last overload under the assumption that it is the "most permissive catch-all case".  This assumption is not always true.  When it is not true, `Parameters` is not useful.

The proposed type could be used as the parameter signature of implementation of the function intersection.

#### Case 2.2.2 The function type is an overload type with the catch-all case

```ts
// Case 2.2.2
type POverloaWithCover2 = Parameters< {
    (x:1|2):void;
    (x:2|3):void;
    (x:1|2|3):void;
}>;
// POverloaWitnCover2 = [x: 1 | 2 | 3]
// Proposed: [x: 1 | 2] | [x: 2 | 3] | [x: 1 | 2 | 3]
```
In this case the proposed type and the current type are the same, and may serve as the parameter signature of implementation of the function intersection.

#### Methods of unioned/intersected Object types

Methods of unioned/intersected Object types fall within the cases described above.

```ts
interface X {
    p: X;
    f(x:X):void;
};
interface Y{
    p: Y;
    f(x:Y):void;
};

// Case 1:
type P1 = Parameters<(X|Y)["f"]>;
// P1 = [x: X] | [x: Y]
// Proposed: [x: X] & [x: Y]

// Case 2.1:
type P2 = Parameters<(X&Y)["f"]>;
// P2 = [x: Y]
// Proposed: [x: X] | [x: Y]
```

### 📃 Motivating Example

As shown above.

### 💻 Use Cases

- It is currently impossible to use `Parameters` to get the correct type of a union of functions.
- It is currently impossible to use `Parameters` to get the implementation signature type of an intersection of functions. (Although there are cases where it works, that is not guaranteed.)
- It is currently impossible to use `Parameters` to get the implementation signature type of a function overload. (Although there are cases where it works, that is not guaranteed.)