在redux 4.0中使用TypeScript键入商店增强器时出现问题

时间:2018-05-21 15:18:08

标签: typescript redux

有些TypeScript主人可以帮我弄清楚为什么在redux 4.0中创建商店增强器的代码给我类型错误?

最小回购是here。它创造了一个无所事事的商店ehnahcer。克隆后,通过运行yarn来安装依赖项。要查看错误,请运行yarn run compile

相关文件是

import {
  StoreEnhancer,
  StoreEnhancerStoreCreator,
  Reducer,
  DeepPartial,
  AnyAction,
} from 'redux';

interface RootState {
  someKey: string;
}

export const enhancer: StoreEnhancer =
  (createStore: StoreEnhancerStoreCreator): StoreEnhancerStoreCreator =>
    (reducer: Reducer<RootState, AnyAction>, preloadedState?: DeepPartial<RootState>) => {

  const store = createStore(reducer, preloadedState);
  const newDispatch: typeof store.dispatch = <A extends AnyAction>(action: A) => {
    const result = store.dispatch(action);
    return result;
  }
  return {
    ...store,
    dispatch: newDispatch,
  };
};

我得到的错误是

src/enhancer.ts(15,5): error TS2322: Type '(reducer: Reducer<RootState, AnyAction>, preloadedState?: DeepPartial<RootState> | undefined) => ...' is not assignable to type 'StoreEnhancerStoreCreator<{}, {}>'.
  Types of parameters 'reducer' and 'reducer' are incompatible.
    Types of parameters 'state' and 'state' are incompatible.
      Type 'RootState | undefined' is not assignable to type 'S | undefined'.
        Type 'RootState' is not assignable to type 'S | undefined'.
          Type 'RootState' is not assignable to type 'S'.

我不明白为什么RootState无法分配给S

1 个答案:

答案 0 :(得分:2)

深入探讨StoreEnhancerStoreCreator的定义方式

export type StoreEnhancerStoreCreator<Ext = {}, StateExt = {}> = <S = any, A extends Action = AnyAction>(reducer: Reducer<S, A>, preloadedState?: DeepPartial<S>) => Store<S & StateExt, A> & Ext;

这是具有两个类型参数ExtStateExt的泛型类型。此类型的结果是另一个通用函数。例如,如果我们这样做

const genericStoreCreator: StoreEnhancerStoreCreator = <S = any, A extends Action = AnyAction>(r: Reducer<S, A>) => createStore(r)

genericStoreCreator仍然是 generic 函数。我们已经使用了通用类型StoreEnhancerStoreCreator,而没有为其提供类型参数(具有默认类型值),但是结果仍然是通用函数。

然后我们可以调用genericStoreCreator,为SA提供特定的类型参数。

const store = genericStoreCreator((state: RootState = { someKey: 'someKey' }, action: AnyAction) => state)

现在我们有了没有任何泛型的特定store

您可以将StoreEnhancerStoreCreator视为泛型。

现在让我们回到存储增强器。来自source(在文档中找不到)

商店增强器是一种高级功能,它构成商店创建者以返回新的增强型商店创建者。

因此,商店增强器的结果仍然应该是通用商店创建者。在增强器函数内部,我们不应该与状态的特定形状(即,具有someKey属性)或动作的特定形状(除具有type属性之外)有关。实施增强器时,我们不知道它将增强什么存储。

要使您的代码正常工作,只需在增强器代码中包含泛型

export const enhancer: StoreEnhancer =
  (createStore: StoreEnhancerStoreCreator): StoreEnhancerStoreCreator =>
    <S = any, A extends Action = AnyAction>(reducer: Reducer<S, A>, preloadedState?: DeepPartial<RootState>) => {

  const store = createStore(reducer, preloadedState);
  const newDispatch: Dispatch<A> = (action) => {
    const result = store.dispatch(action);
    return result;
  }
  return {
    ...store,
    dispatch: newDispatch,
   };
};

请注意,增强型商店创建者仍是通用的,可以成为具有任何状态和动作的用户。

但是我们可以用增强剂增强什么呢?为source says

@template Ext与商店类型混合的商店扩展名。

@template StateExt与状态类型混合的状态扩展。

让我们使用print5函数扩展存储,该函数在调用时将记录5到控制台。

export const enhancer: StoreEnhancer<{ print5: () => void }> =
  (createStore: StoreEnhancerStoreCreator): StoreEnhancerStoreCreator<{ print5: () => void }> =>
    <S = any, A extends Action = AnyAction>(reducer: Reducer<S, A>, preloadedState?: DeepPartial<RootState>) => {

  const store = createStore(reducer, preloadedState);
  const newDispatch: Dispatch<A> = (action) => {
    const result = store.dispatch(action);
    return result;
  }
  return {
    ...store,
    dispatch: newDispatch,
    print5 () { console.log(5) },
   };
  };

再次记下结果存储仍然是通用的。

这是简单的demo。记下print5函数如何从增强器传播到结果存储。