Repository: TypeScript
Issue: JS generic inference change after upgrading to 5.1
Labels: Needs Investigation,Rescheduled

# Bug Report
While working on upgrading Typescript to 5.1. from 4.7, I came across an issue with how generics are inferred with JS. Or, potentially not an _issue_, but a change in how it works that breaks existing code.

Importantly, this issue happens when you have a TS file importing a JS function importing a TS function. Previously, inference worked "good enough" for this scenario to work, but the change breaks it. (Unfortunately, this is a large, older codebase, so we migrate parts to TS as we can! In this case, there's a Typescript utility function, a JS redux selector using the utility, and a Typescript React component using the selector. But the only part that matters is the generic inference and having the JS file.)

### 🔎 Search Terms
Generics, arguments, JavaScript inference

### 🕗 Version & Regression Information

<!-- When did you start seeing this bug occur?

"Bugs" that have existed in TS for a long time are very likely to be FAQs; refer to
  https://github.com/Microsoft/TypeScript/wiki/FAQ#common-bugs-that-arent-bugs

If possible, please try testing the nightly version of TS to see if it's already been fixed.
For npm: `typescript@next`
This is also the 'Nightly' version in the playground: http://www.typescriptlang.org/play/?ts=Nightly

Note: The TypeScript Playground can be used to try older versions of TypeScript.

Please keep and fill in the line that best applies:
-->
- This changed between versions 4.7.4 and 5.1.6

### ⏯ Playground Link

Since this requires JS code in between to TS files, I can't replicate it in the TS playground. However, you can create the three short files I mentioned below and you'll see the issue! That said, here's the [working code in full TS](https://www.typescriptlang.org/play?#code/KYDwDg9gTgLgBAMwK4DsDGMCWEVzVYAQxmAGVgAbYDaAHjgBVSZjgAaRgBSgjAGc4oEigAmAwigCeAbQC6HBgBFgUTADdgI5qzgA+ABQAoAJB9K1GNABccfXD4sSNpo-ZwAdJ7A9+z7rwEASjgAXl1GZVUNLVc2EwBzYBhlMGBRNJg+GzsHVmdtEg5Pd28Avx8g0PDUAGsUCAB3FENA7PtXfNiPLwrygLhgsIiVdU0C4DgAbxMCGCQoXByu4sIoeMqh6eNjROTgVPSUTKXWIs9V9YGAbhNjWfncMyoaKBPC7vcLoJvjAF8b36GQwwSSpODjUJwABK1GgInoKCQAFsAEYqDgOVQoeK6G6GUCQWB4HAOOC7ABCkgAkiJIfgiCRyM9LK9DHBbO08uCupgRDZEaiVAMqpySNI4Ly4PI2RzciRhUM5cAWni0CT4CJiIRIRTqSI7JM4ABGGwAchIDlNcF+HCN1yAA). All that needs to change is for `getById` to be written in JS, and the error occurs.

### 💻 Code

Consider this simplified higher order function. Fully implemented, it would be an optimized redux-style selector that only recalculates the selector when the dependents change. Both the selector and dependents have the same signature:

```ts
// create-selector.ts
export function createSelector< TState, TProps extends any[], TDerivedState >(
	selector: ( state: TState, ...props: TProps ) => TDerivedState,
	getDependents: ( state: TState, ...props: TProps ) => unknown
): ( state: TState, ...props: TProps ) => TDerivedState {
	return ( state, ...args ) => {
		getDependents( state, ...args );
		return selector( state, ...args );
	};
}
```

Here's a simple _JS_ file with a new selector using that function:
```js
// get-by-id.js
import { createSelector } from './create-selector';
export const getById = createSelector(
  ( state, id ) => state[ id ],
  ( state ) => state // the other arguments to the selector aren't necessary for dependents here
);
```

And here's a _TS_ file where the selector is used:
```ts
import { getById } from './get-by-id';

// ERROR: Expected 1 argument, but got 2.
getById( { 1: 'test' }, 1 );
```

### 🙁 Actual behavior

The error is that `getById` expects the incorrect number of arguments. It can accept 2 arguments, but the type inference restricts it to 1. The issue is that in the JS file, the two argument functions have different signatures. Typescript infers the `TProps` type based on the **second function** -- so `getById` is inferred as `(state: any) => any`.

Interestingly, this is only an issue when the selector is written in JS, not TS. When `getById` is implemented in TS 5.1, it uses **the first function** to infer the type of `getById` -- so it becomes `(state: State, id: number ) => string`.

In other words, **generic inference is inconsistent** depending on if the function comes from JS or TS, despite most of the type information coming from a TS file in the first place!

I've not been able to find a workaround -- if we change to `Partial< TProps >` for the signature of `getDependents`, we encounter issues in other selectors where the args are always defined (since Partial means some of those args can be undefined). 

### 🙂 Expected behavior

Before the update, Typescript seemingly inferred `TProps` based on **both functions** -- so `getById` becomes `(state: any, id?: any) => any` .

Now, I can understand that this situation is a bit tricky, and inferring based on both functions isn't ideal either. But I'm not sure how to express the types better here. `getDependents` will be called with all of the arguments, but it's not strictly necessary for the function body to accept all of those arguments when they might not be used. (And either way, this scenario works in TS, which indicates the basic premise is accepted by TS.)