Repository: TypeScript
Issue: Incorrect type inference at function callsite - wrapping type inside of a Tuple
Labels: Needs Investigation

<!-- 🚨 STOP 🚨 STOP 🚨 STOP 🚨

Half of all issues filed here are duplicates, answered in the FAQ, or not appropriate for the bug tracker. Even if you think you've found a *bug*, please read the FAQ first, especially the Common "Bugs" That Aren't Bugs section!

Please help us by doing the following steps before logging an issue:
  * Search: https://github.com/Microsoft/TypeScript/search?type=Issues
  * Read the FAQ: https://github.com/Microsoft/TypeScript/wiki/FAQ

Please fill in the *entire* template below.
-->

<!--
Please try to reproduce the issue with the latest published version. It may have already been fixed.
For npm: `typescript@next`
This is also the 'Nightly' version in the playground: http://www.typescriptlang.org/play/?ts=Nightly
-->
**TypeScript Version:**  4.0.5
Tested this on `v4.1.0-beta`, same issue (in playground)
Tested on Nightly (`v4.2.0-dev.20201112`), same issue (in playground)
Tested on `v3.9.7`, same issue (in playground)
Tested on `v3.8.3`, same issue (in playground)
Tested on `v3.7.5`, same issue (in playground)

<!-- Search terms you tried before logging this (so others can find this issue more easily) -->
**Search Terms:**
tuple inference
argument not assignable
ts2345

**Code**

This is inside of a react typescript project, when upgrading `react-scripts` along with `typescript` to the latest (v4) versions.

We have a generic custom react hook that is having some tsc errors after upgrading - this one though I haven't been able to work through and it looks like a bug of some kind in how tsc is inferring the type.

```ts
import {
	useReducer,
	useRef,
	useLayoutEffect,
	useMemo,
        Reducer,
} from 'react'; //react: ^16.8.6
import _ from 'lodash'; //lodash: ^4.17.20

export interface IOptions {    
	/**
	 * The number of milliseconds to debounce the request. Defaults to no debounce.
	 * When `true`, defaults to 300 milliseconds.
	 */
	debounce?: boolean | number;
}

export interface ICallbacks<T> {
}

export interface IResult<T> {
	isLoading: boolean;
	response?: T;
	error?: any;
}

export interface IReturn<T> extends IResult<T> {
	refetch(): void;
}

type Action<T> =
	| { type: 'REQUEST' }
	| { type: 'SUCCESS'; payload: T }
	| { type: 'FAILURE'; payload: any };

const initialResults: IResult<any> = {
	isLoading: false,
	response: undefined,
	error: undefined,
};

function resultsReducer<T>(
	state: IResult<T>,
	action: Action<T>,
): IResult<T> {
	switch (action.type) {
		case 'REQUEST':
			return { isLoading: true, response: state.response };
		case 'SUCCESS':
			return { isLoading: false, response: action.payload };
		case 'FAILURE':
			return { isLoading: false, error: action.payload };
		default:
			return initialResults;
	}
}

function isCancellable<T = any>(
	fetch: RequestSender<T>,
): fetch is DebouncedRequestSender<T> {
	if ((fetch as any).cancel) return true;

	return false;
}

type DebouncedRequestSender<T = any> = _.DebouncedFunc<
	(
		url: string,
		options: IOptions,
		callbacks: ICallbacks<T>,
		dispatch: DispatchFunc<Action<T>>,
	) => () => void
>;

type RequestSender<T = any> = DebouncedRequestSender<T> | typeof performRequest;

function performRequest<T = any>(
	url: string,
	options: IOptions,
	callbacks: ICallbacks<T>,
	dispatch: React.Dispatch<Action<T>>,
): (() => void) {
	dispatch({ type: 'REQUEST' });

	return () => {};
};

export function useHook<T = any>(
	url: string,
	options: IOptions = {},
	callbacks: ICallbacks<T> = {},
): void {
	const [results, dispatch] = useReducer<
		Reducer<IResult<T>, Action<T>>
	>(resultsReducer, initialResults);

    // Use a ref to hold onto the callbacks. The prevents us from having to
	// include the callbacks in the useEffect dependencies.
	const callbacksRef = useRef(callbacks);
	callbacksRef.current = callbacks;

    // Create a debounced function, if needed. Also, memoize this function so
	// that it doesn't cause unnecessary requests.
	const fetch: RequestSender<T> = useMemo(() => {
		if (options.debounce) {
			// Use a default number of milliseconds if debounce is `true`.
			const wait = options.debounce === true ? 300 : options.debounce;
			return _.debounce(performRequest, wait);
		} else {
			return performRequest;
		}
	}, [options.debounce]);

    useLayoutEffect(() => {
		const cancelFetch: (() => void) | undefined = fetch(
			url,
			options,
			callbacksRef.current,
			dispatch,
		);

		return () => {
			// Cancel the debounced function call, if needed.
			if (isCancellable(fetch)) fetch.cancel();
		};
	}, [url, options, fetch]);

}  
```

**Expected behavior:**
I would expect this not to result in a tsc error. I'm not sure where it is inferring this tuple that is wrapping the actual type for the `dispatch` variable, but based on what intellisense is showing me in VS Code, the `dispatch` variable's type matches the expected type for the `fetch` function signature.

**Actual behavior:**
`Argument of type '[Dispatch<Action<T>>]' is not assignable to parameter of type 'Dispatch<Action<T>>'.
  Type '[Dispatch<Action<T>>]' provides no match for the signature '(value: Action<T>): void'.ts(2345)`

**Playground Link:** 
[TS Playground link](https://www.typescriptlang.org/play?#code/PTAEHUFMBsGMHsC2lQBd5oBYoCoE8AHSAZVgCcBLA1UABWgEM8BzM+AVwDsATAGiwoBnUENANQAd0gAjQRVSQAUCEmYKsTKGYUAbpGF4OY0BoadYKdJMoL+gzAzIoz3UNEiPOofEVKVqAHSKFIgE8GQ0AN6KAJDsgpAASpDc7BZkvLHxSZAAZplxCQAyTByoAKK5uZCwqAXZALKQiPCZAL6guWyIoADkTgy1vQDcwaHhNAD6nd190PDcDPYjisrAyYOoAQAiQgQMqBqKqIQou4L7h5gAYlywADwAggB8oAC8oAAUOgzQ7JAALlAjwAlO9Xjp4BRuKM1htagFkql0sdTqAkWlIGR7sR+C93l8CE4dMRUAdAaBcWJahR4JwgaDwZTYSB5ot7DsZBxzClbuZgpwFGRcoMzly7ry7vccKBIAAPBQ8YSfAKqxzMQRAsx4ADaAF0wW9XtrXtFQOaLebgAAqa2KS0O62gADCv2gWBQ4Qo2k4v06d1QtM4-Gk7BoDAIBGgeAonGYHtA3HFPNAZHY7kEQQdlrt2YtToAkrkE0npNyLK5cgGgyYzKBpCg014QshuBRydH+Kg1MJYG7hPIxDxU5BUOwyJwB6hhE4xxP7XnQE6fn9IFnF7mN6AAPLdrESISQfiD2fjycJ08T0Ar-6geDFvduJY0WOQgDWByD-HCoC4SdysYpCID7YImyYVgueZOlW5iBnSkhLKAnDwC+nDvkBeCjuuUHAJBDoqmqZAakCtCOAwyBCoI0rPCCQLJHOnA+JA1GgAAPr+PB5IBMKrIuNqblB3iYGwEhiBITBDngoBEDwsbxq+8AfnBXj3iW4FATBNJ0th2bWrhi59jy0CfLR15QjxeGWvxlk5qARYek4IjCOIMltnGIhoYpn7wapj6luWGnVnSx6eW+KCDi2KTtgo0ZDq4l6cDZ+YiNOI4Mdevz-DpjpJeaTq7tgZAHgk-AJQmN4oF0SAJowgioe+3nBneZAcf+3HAWpZYSpWQWJYuS4IcInCQHoLUKWF3DZTm+l5rkfz2CZdGjmeTEsexf5ccNPGWm0qzymEEQeUKIoWHZ27UEGwiRJasTWTEA04KBnDsIgDYtapiAUNA0CHggSpoBg-kShekAAI7-HVASgNseQMOmqVWMhYFdTyQT3U64DYF4AAGqBppA2P8P+cPQAjGAAMwAAyU6An3fb9dLcJmsRLrhMRAzyAD8QJlvA7h1uxz2vVioy7Yo+0TEdWInSgBaut90iDG+VE4KaihixLh2xsdop2ckgjwyx0QxEIRTwAwbnMDz8B8x4nCjDETgXHSCTc94DtYmwZBu9qot7XKB2oTrp0FvRZ4sfKipM3r+iG6roDG041RXItZnQn7qJEMCWmcCxbyxOx10nEQQK9Ik5QAIoAKrlMQOC9KAu0xIXaCnKXxBV86zq18QIzSUw8wW0CMpNy3xcUr01yPAWRRV+Xff7Hgg-cFqnBSW0sJ-XVHnyO20D6-DmoxwbpP3CaBLG6b5uW0CIrQCVsRO2Ek4UhtAFbQUnvhECb-ce0sKaWUiOE+04MTpGop8WIdVyRAlDrHU+qsCibCDAyHO1FMimTgSAo2UCDxXC+Mg7S48wTGxiH2BIfRy7V1rvXAEsQYiO2Wlea6V8LZySBHjf4pV9DPwSECaBCgAhPxdigDe9DyEoF6B3LuPdeh0IYYw9KLDBBmzYXGW+vwSrAN4RSQhnAAiL2Xo3B2ZCliSKnjPOe5Q5H0MUWeBOTlVE306Joo8soyBey1DnAxA9r7GPocTeG8iGFlVjLvX4B9SaCAdrtMWgCaxCFdEZRg0h3DSgJCaSBMRk4aCWuDfQqBiCQE4tiRBihTI5M0KIGGKMKzJHyXVIpJScEm2LJ8T4lSxDOTXiCAIhkLDQDBGVThkBYR2KvHfBIGdx7Q3UtwepENCnFKTKUjJa9XgfBqQFbgfIHixCyXEMg0B+F4zkgUGI8ALou1gedZSghzl9gVkrI+cs3SK1gMrdBAS9gHFydDH5VxdlPDQarZ4BRDSvBMkySE0JFDPFhDMhZBSmkrPSR8c+my5lIsacsrEa1W5EFUkQYU4REDYtQAA3q0lpakvJWiySzwsnjmOaAOqlA4wFEuXcm5VzJwFEedAd5ytYHy0Fc8r57MAV-POJcDQQLHggueGC8pQJ2kQrTtwEhsQ2wXF+ZgT4Rc26UMrjXOuDc2ggjGWVKFRoE5iLEeLAOkt4nwWyAACRtm+elmSshHJOey5gnLeUvNuZdC+bR+VvOeSKqNHyVYbLtRgoEMLXDGy3jQHUTtD5EylZgPUBJshgLxfQot2IsFxzBdnZS1FnixEZVmqJpaQrhP3vA6clreIWhUFXCh4gk4A1AJgPmrg6RWEfAKoVmYhIoCJCNYpqV4gzGqg4HQckAa3TALGWAfwkwJgnc8jyCZsiVGqLUMCrlimwAoPoNG6baxPLjckYsHxC15E+PuuNHbTEPuVk+vp44nCCgJB+5WsIrJgGdAMBQxgOYVn9LBL8HVhopBSFDR499Wi02aFCAAXpYHs8Gc6svgBurABwUqJngPoTgvQaB9myBxYaFhBCCEcFJJwDTpy3pdjQSpeTFkorxfHF9CQmgtDVUyUhFA2lcsugEWDkAtUKO7b2sCIp4ZIRem9O8xY6Y-QSH9aO0nkYBScqAXG+NsZowYXe8Sg4PiyZdvJ9S7w3gfBGaATmoAqY0yBI5ycznamjNsWVSYgWAqfGJbkWlYNFn8Ds6gL9MQOgwAoaQ8ZXgosxc4yYpuEbQA6n85mBTBowMWmyCUQwYYT01FQBJ21pC739JgNcUcfz6sQnMmCdanF35AQ+JUg5hzoDnIucG0bIHBB-tgAB+do2dWyswOcjt9DrXqvSyoJJAzOrbMI0AgVx5izIaTJNWxxnPiJLMAMlJ7gOltcwCCMElS+lXZgCZXLMT+A6mZd+cbnR7uldWLtc0QA)

**Related Issues:** <!-- Did you find other bugs that looked similar? -->