Skip to content

Commit cd041b1

Browse files
committed
fix(connect): split mapStateToProps/mapDispatchToProps unions into ordered overloads (reduxjs#2244)
1 parent dc531f4 commit cd041b1

2 files changed

Lines changed: 377 additions & 16 deletions

File tree

src/components/connect.tsx

Lines changed: 186 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,14 @@ import type {
1515
} from '../types'
1616

1717
import type {
18+
MapStateToProps,
19+
MapStateToPropsFactory,
1820
MapStateToPropsParam,
21+
MapDispatchToProps,
22+
MapDispatchToPropsFactory,
23+
MapDispatchToPropsFunction,
1924
MapDispatchToPropsParam,
2025
MergeProps,
21-
MapDispatchToPropsNonObject,
2226
SelectorFactoryOptions,
2327
} from '../connect/selectorFactory'
2428
import defaultSelectorFactory from '../connect/selectorFactory'
@@ -279,47 +283,128 @@ export interface ConnectOptions<
279283
*/
280284
export interface Connect<DefaultState = unknown> {
281285
// tslint:disable:no-unnecessary-generics
286+
//
287+
// NOTE: the `mapStateToProps` and `mapDispatchToProps` parameters are
288+
// intentionally split into separate "factory" and "plain" overloads instead of
289+
// accepting the `MapStateToPropsParam` / `MapDispatchToProps{Param,NonObject}`
290+
// unions directly. A factory (e.g. `() => (state) => props`) is structurally
291+
// assignable to its plain counterpart, so when both forms live in a single
292+
// union the compiler has to pick a "first" inference candidate - and the old
293+
// and new (native, TS 7 / `tsgo`) compilers order union members differently,
294+
// inferring `TStateProps` / `TDispatchProps` incorrectly on one of them.
295+
// Listing the factory overload first makes the resolution order explicit and
296+
// compiler-independent. The `mergeProps` overloads keep the unions on purpose
297+
// (see the note on those overloads below).
298+
// See https://github.com/reduxjs/react-redux/issues/2244
282299
(): InferableComponentEnhancer<DispatchProp>
283300

301+
/** mapState only (as a factory) */
302+
<TStateProps = {}, no_dispatch = {}, TOwnProps = {}, State = DefaultState>(
303+
mapStateToProps: MapStateToPropsFactory<TStateProps, TOwnProps, State>,
304+
): InferableComponentEnhancerWithProps<TStateProps & DispatchProp, TOwnProps>
305+
284306
/** mapState only */
285307
<TStateProps = {}, no_dispatch = {}, TOwnProps = {}, State = DefaultState>(
286-
mapStateToProps: MapStateToPropsParam<TStateProps, TOwnProps, State>,
308+
mapStateToProps:
309+
| MapStateToProps<TStateProps, TOwnProps, State>
310+
| null
311+
| undefined,
287312
): InferableComponentEnhancerWithProps<TStateProps & DispatchProp, TOwnProps>
288313

314+
/** mapDispatch only (as a factory) */
315+
<no_state = {}, TDispatchProps = {}, TOwnProps = {}>(
316+
mapStateToProps: null | undefined,
317+
mapDispatchToProps: MapDispatchToPropsFactory<TDispatchProps, TOwnProps>,
318+
): InferableComponentEnhancerWithProps<TDispatchProps, TOwnProps>
319+
289320
/** mapDispatch only (as a function) */
290321
<no_state = {}, TDispatchProps = {}, TOwnProps = {}>(
291322
mapStateToProps: null | undefined,
292-
mapDispatchToProps: MapDispatchToPropsNonObject<TDispatchProps, TOwnProps>,
323+
mapDispatchToProps: MapDispatchToPropsFunction<TDispatchProps, TOwnProps>,
293324
): InferableComponentEnhancerWithProps<TDispatchProps, TOwnProps>
294325

295326
/** mapDispatch only (as an object) */
296327
<no_state = {}, TDispatchProps = {}, TOwnProps = {}>(
297328
mapStateToProps: null | undefined,
298-
mapDispatchToProps: MapDispatchToPropsParam<TDispatchProps, TOwnProps>,
329+
mapDispatchToProps: MapDispatchToProps<TDispatchProps, TOwnProps>,
299330
): InferableComponentEnhancerWithProps<
300331
ResolveThunks<TDispatchProps>,
301332
TOwnProps
302333
>
303334

335+
/** mapState (as a factory) and mapDispatch (as a factory) */
336+
<TStateProps = {}, TDispatchProps = {}, TOwnProps = {}, State = DefaultState>(
337+
mapStateToProps: MapStateToPropsFactory<TStateProps, TOwnProps, State>,
338+
mapDispatchToProps: MapDispatchToPropsFactory<TDispatchProps, TOwnProps>,
339+
): InferableComponentEnhancerWithProps<
340+
TStateProps & TDispatchProps,
341+
TOwnProps
342+
>
343+
344+
/** mapState (as a factory) and mapDispatch (as a function) */
345+
<TStateProps = {}, TDispatchProps = {}, TOwnProps = {}, State = DefaultState>(
346+
mapStateToProps: MapStateToPropsFactory<TStateProps, TOwnProps, State>,
347+
mapDispatchToProps: MapDispatchToPropsFunction<TDispatchProps, TOwnProps>,
348+
): InferableComponentEnhancerWithProps<
349+
TStateProps & TDispatchProps,
350+
TOwnProps
351+
>
352+
353+
/** mapState and mapDispatch (as a factory) */
354+
<TStateProps = {}, TDispatchProps = {}, TOwnProps = {}, State = DefaultState>(
355+
mapStateToProps:
356+
| MapStateToProps<TStateProps, TOwnProps, State>
357+
| null
358+
| undefined,
359+
mapDispatchToProps: MapDispatchToPropsFactory<TDispatchProps, TOwnProps>,
360+
): InferableComponentEnhancerWithProps<
361+
TStateProps & TDispatchProps,
362+
TOwnProps
363+
>
364+
304365
/** mapState and mapDispatch (as a function)*/
305366
<TStateProps = {}, TDispatchProps = {}, TOwnProps = {}, State = DefaultState>(
306-
mapStateToProps: MapStateToPropsParam<TStateProps, TOwnProps, State>,
307-
mapDispatchToProps: MapDispatchToPropsNonObject<TDispatchProps, TOwnProps>,
367+
mapStateToProps:
368+
| MapStateToProps<TStateProps, TOwnProps, State>
369+
| null
370+
| undefined,
371+
mapDispatchToProps: MapDispatchToPropsFunction<TDispatchProps, TOwnProps>,
308372
): InferableComponentEnhancerWithProps<
309373
TStateProps & TDispatchProps,
310374
TOwnProps
311375
>
312376

377+
/** mapState (as a factory) and mapDispatch (nullish) */
378+
<TStateProps = {}, TDispatchProps = {}, TOwnProps = {}, State = DefaultState>(
379+
mapStateToProps: MapStateToPropsFactory<TStateProps, TOwnProps, State>,
380+
mapDispatchToProps: null | undefined,
381+
): InferableComponentEnhancerWithProps<TStateProps, TOwnProps>
382+
313383
/** mapState and mapDispatch (nullish) */
314384
<TStateProps = {}, TDispatchProps = {}, TOwnProps = {}, State = DefaultState>(
315-
mapStateToProps: MapStateToPropsParam<TStateProps, TOwnProps, State>,
385+
mapStateToProps:
386+
| MapStateToProps<TStateProps, TOwnProps, State>
387+
| null
388+
| undefined,
316389
mapDispatchToProps: null | undefined,
317390
): InferableComponentEnhancerWithProps<TStateProps, TOwnProps>
318391

392+
/** mapState (as a factory) and mapDispatch (as an object) */
393+
<TStateProps = {}, TDispatchProps = {}, TOwnProps = {}, State = DefaultState>(
394+
mapStateToProps: MapStateToPropsFactory<TStateProps, TOwnProps, State>,
395+
mapDispatchToProps: MapDispatchToProps<TDispatchProps, TOwnProps>,
396+
): InferableComponentEnhancerWithProps<
397+
TStateProps & ResolveThunks<TDispatchProps>,
398+
TOwnProps
399+
>
400+
319401
/** mapState and mapDispatch (as an object) */
320402
<TStateProps = {}, TDispatchProps = {}, TOwnProps = {}, State = DefaultState>(
321-
mapStateToProps: MapStateToPropsParam<TStateProps, TOwnProps, State>,
322-
mapDispatchToProps: MapDispatchToPropsParam<TDispatchProps, TOwnProps>,
403+
mapStateToProps:
404+
| MapStateToProps<TStateProps, TOwnProps, State>
405+
| null
406+
| undefined,
407+
mapDispatchToProps: MapDispatchToProps<TDispatchProps, TOwnProps>,
323408
): InferableComponentEnhancerWithProps<
324409
TStateProps & ResolveThunks<TDispatchProps>,
325410
TOwnProps
@@ -332,6 +417,10 @@ export interface Connect<DefaultState = unknown> {
332417
mergeProps: MergeProps<undefined, DispatchProp, TOwnProps, TMergedProps>,
333418
): InferableComponentEnhancerWithProps<TMergedProps, TOwnProps>
334419

420+
// `mergeProps` overloads keep the union parameters (see the note on the final
421+
// `mergeProps` overload below) - splitting off a factory overload would
422+
// collapse the inferred props to `{}`.
423+
335424
/** mapState and mergeProps */
336425
<
337426
TStateProps = {},
@@ -352,55 +441,136 @@ export interface Connect<DefaultState = unknown> {
352441
mergeProps: MergeProps<undefined, TDispatchProps, TOwnProps, TMergedProps>,
353442
): InferableComponentEnhancerWithProps<TMergedProps, TOwnProps>
354443

444+
/** mapState (as a factory) and options */
445+
<TStateProps = {}, no_dispatch = {}, TOwnProps = {}, State = DefaultState>(
446+
mapStateToProps: MapStateToPropsFactory<TStateProps, TOwnProps, State>,
447+
mapDispatchToProps: null | undefined,
448+
mergeProps: null | undefined,
449+
options: ConnectOptions<State, TStateProps, TOwnProps>,
450+
): InferableComponentEnhancerWithProps<DispatchProp & TStateProps, TOwnProps>
451+
355452
/** mapState and options */
356453
<TStateProps = {}, no_dispatch = {}, TOwnProps = {}, State = DefaultState>(
357-
mapStateToProps: MapStateToPropsParam<TStateProps, TOwnProps, State>,
454+
mapStateToProps:
455+
| MapStateToProps<TStateProps, TOwnProps, State>
456+
| null
457+
| undefined,
358458
mapDispatchToProps: null | undefined,
359459
mergeProps: null | undefined,
360460
options: ConnectOptions<State, TStateProps, TOwnProps>,
361461
): InferableComponentEnhancerWithProps<DispatchProp & TStateProps, TOwnProps>
362462

463+
/** mapDispatch (as a factory) and options */
464+
<TStateProps = {}, TDispatchProps = {}, TOwnProps = {}>(
465+
mapStateToProps: null | undefined,
466+
mapDispatchToProps: MapDispatchToPropsFactory<TDispatchProps, TOwnProps>,
467+
mergeProps: null | undefined,
468+
options: ConnectOptions<{}, TStateProps, TOwnProps>,
469+
): InferableComponentEnhancerWithProps<TDispatchProps, TOwnProps>
470+
363471
/** mapDispatch (as a function) and options */
364472
<TStateProps = {}, TDispatchProps = {}, TOwnProps = {}>(
365473
mapStateToProps: null | undefined,
366-
mapDispatchToProps: MapDispatchToPropsNonObject<TDispatchProps, TOwnProps>,
474+
mapDispatchToProps: MapDispatchToPropsFunction<TDispatchProps, TOwnProps>,
367475
mergeProps: null | undefined,
368476
options: ConnectOptions<{}, TStateProps, TOwnProps>,
369477
): InferableComponentEnhancerWithProps<TDispatchProps, TOwnProps>
370478

371479
/** mapDispatch (as an object) and options*/
372480
<TStateProps = {}, TDispatchProps = {}, TOwnProps = {}>(
373481
mapStateToProps: null | undefined,
374-
mapDispatchToProps: MapDispatchToPropsParam<TDispatchProps, TOwnProps>,
482+
mapDispatchToProps: MapDispatchToProps<TDispatchProps, TOwnProps>,
375483
mergeProps: null | undefined,
376484
options: ConnectOptions<{}, TStateProps, TOwnProps>,
377485
): InferableComponentEnhancerWithProps<
378486
ResolveThunks<TDispatchProps>,
379487
TOwnProps
380488
>
381489

490+
/** mapState (as a factory), mapDispatch (as a factory), and options */
491+
<TStateProps = {}, TDispatchProps = {}, TOwnProps = {}, State = DefaultState>(
492+
mapStateToProps: MapStateToPropsFactory<TStateProps, TOwnProps, State>,
493+
mapDispatchToProps: MapDispatchToPropsFactory<TDispatchProps, TOwnProps>,
494+
mergeProps: null | undefined,
495+
options: ConnectOptions<State, TStateProps, TOwnProps>,
496+
): InferableComponentEnhancerWithProps<
497+
TStateProps & TDispatchProps,
498+
TOwnProps
499+
>
500+
501+
/** mapState (as a factory), mapDispatch (as a function), and options */
502+
<TStateProps = {}, TDispatchProps = {}, TOwnProps = {}, State = DefaultState>(
503+
mapStateToProps: MapStateToPropsFactory<TStateProps, TOwnProps, State>,
504+
mapDispatchToProps: MapDispatchToPropsFunction<TDispatchProps, TOwnProps>,
505+
mergeProps: null | undefined,
506+
options: ConnectOptions<State, TStateProps, TOwnProps>,
507+
): InferableComponentEnhancerWithProps<
508+
TStateProps & TDispatchProps,
509+
TOwnProps
510+
>
511+
512+
/** mapState, mapDispatch (as a factory), and options */
513+
<TStateProps = {}, TDispatchProps = {}, TOwnProps = {}, State = DefaultState>(
514+
mapStateToProps:
515+
| MapStateToProps<TStateProps, TOwnProps, State>
516+
| null
517+
| undefined,
518+
mapDispatchToProps: MapDispatchToPropsFactory<TDispatchProps, TOwnProps>,
519+
mergeProps: null | undefined,
520+
options: ConnectOptions<State, TStateProps, TOwnProps>,
521+
): InferableComponentEnhancerWithProps<
522+
TStateProps & TDispatchProps,
523+
TOwnProps
524+
>
525+
382526
/** mapState, mapDispatch (as a function), and options */
383527
<TStateProps = {}, TDispatchProps = {}, TOwnProps = {}, State = DefaultState>(
384-
mapStateToProps: MapStateToPropsParam<TStateProps, TOwnProps, State>,
385-
mapDispatchToProps: MapDispatchToPropsNonObject<TDispatchProps, TOwnProps>,
528+
mapStateToProps:
529+
| MapStateToProps<TStateProps, TOwnProps, State>
530+
| null
531+
| undefined,
532+
mapDispatchToProps: MapDispatchToPropsFunction<TDispatchProps, TOwnProps>,
386533
mergeProps: null | undefined,
387534
options: ConnectOptions<State, TStateProps, TOwnProps>,
388535
): InferableComponentEnhancerWithProps<
389536
TStateProps & TDispatchProps,
390537
TOwnProps
391538
>
392539

540+
/** mapState (as a factory), mapDispatch (as an object), and options */
541+
<TStateProps = {}, TDispatchProps = {}, TOwnProps = {}, State = DefaultState>(
542+
mapStateToProps: MapStateToPropsFactory<TStateProps, TOwnProps, State>,
543+
mapDispatchToProps: MapDispatchToProps<TDispatchProps, TOwnProps>,
544+
mergeProps: null | undefined,
545+
options: ConnectOptions<State, TStateProps, TOwnProps>,
546+
): InferableComponentEnhancerWithProps<
547+
TStateProps & ResolveThunks<TDispatchProps>,
548+
TOwnProps
549+
>
550+
393551
/** mapState, mapDispatch (as an object), and options */
394552
<TStateProps = {}, TDispatchProps = {}, TOwnProps = {}, State = DefaultState>(
395-
mapStateToProps: MapStateToPropsParam<TStateProps, TOwnProps, State>,
396-
mapDispatchToProps: MapDispatchToPropsParam<TDispatchProps, TOwnProps>,
553+
mapStateToProps:
554+
| MapStateToProps<TStateProps, TOwnProps, State>
555+
| null
556+
| undefined,
557+
mapDispatchToProps: MapDispatchToProps<TDispatchProps, TOwnProps>,
397558
mergeProps: null | undefined,
398559
options: ConnectOptions<State, TStateProps, TOwnProps>,
399560
): InferableComponentEnhancerWithProps<
400561
TStateProps & ResolveThunks<TDispatchProps>,
401562
TOwnProps
402563
>
403564

565+
// NOTE: the `mergeProps` overloads keep the union parameters rather than the
566+
// factory/plain split used above. `mergeProps` receives `stateProps` and
567+
// `dispatchProps` as contextually-typed (non-inferring) parameters, so
568+
// `TStateProps` / `TDispatchProps` can only be inferred from `mapStateToProps`
569+
// / `mapDispatchToProps`. With a dedicated factory overload listed first a
570+
// plain map function gets captured by it and the inferred props collapse to
571+
// `{}`. The union keeps both forms as inference candidates in a single
572+
// overload, which is the original (and correct) behavior for these signatures.
573+
404574
/** mapState, mapDispatch, mergeProps, and options */
405575
<
406576
TStateProps = {},

0 commit comments

Comments
 (0)