【问题标题】:How do I relate the generic types defined between higher-order functions?如何关联高阶函数之间定义的泛型类型?
【发布时间】:2018-11-17 16:56:37
【问题描述】:

我正在创建一个 Redux 存储增强器,它接受一个函数来序列化 Redux 状态。我将构建商店并为更改设置订阅——每次更改时,我都会序列化状态。对于这个 MCVE,我忽略了订阅方面,只是立即调用序列化函数。

但是,由于函数的高阶性质,我无法将状态的泛型类型(序列化函数所需)与商店创建者返回的泛型类型相关联:

// Copied and reduced from Redux 4.0.1

type Reducer<S = any> = (
  state: S | undefined,
) => S

type DeepPartial<T> = { [K in keyof T]?: DeepPartial<T[K]> }

interface Store<S = any> {
  getState(): S
}

type StoreEnhancer<Ext = {}, StateExt = {}> = (
  next: StoreEnhancerStoreCreator
) => StoreEnhancerStoreCreator<Ext, StateExt>

type StoreEnhancerStoreCreator<Ext = {}, StateExt = {}> = <
  S = any,
>(
  reducer: Reducer<S>,
  preloadedState?: DeepPartial<S>
) => Store<S & StateExt> & Ext

// My reduced code

interface Config<S> {
  serialize: (state: S) => string;
}

const storage = <S>(config: Config<S>): StoreEnhancer => createStore => (reducer, preloadedState) => {
  const { serialize } = config;

  const theStore = createStore(reducer, preloadedState);

  const state = theStore.getState();
  const serializedState = serialize(state);

  return theStore;
}

(playground)

错误是:

const serializedState = serialize(state);
                                  ^~~~~

Argument of type 'S & {}' is not assignable to parameter of type 'S'.

错误信息很烦人,因为我很确定这两个S 是不相关的;将 StoreEnhancerStoreCreator 中的定义更改为使用 X 而不是 S 会更改此错误消息。

如何将我的泛型类型参数与StoreEnhancerStoreCreator 上定义的泛型参数连接起来?

【问题讨论】:

    标签: typescript generics higher-order-functions


    【解决方案1】:

    我同意@matt-mccutchen 提出的问题。 StoreEnhancerStoreCreator 是泛型函数,所以(reducer, preloadedState) =&gt; { 是带有S 泛型类型参数的泛型函数,这就是编译器报告两种S 类型不兼容的原因。

    我建议的解决方案是StoreEnhancerStoreCreator 不是通用的,据我所知,reducer 和有效负载应该具有与Config 相同的通用类型参数。此解决方案需要在StoreEnhancerStoreCreatorStoreEnhancer 中添加额外的类型参数:

    type Reducer<S = any> = (
      state: S | undefined,
    ) => S
    
    type DeepPartial<T> = { [K in keyof T]?: DeepPartial<T[K]> }
    
    interface Store<S = any> {
      getState(): S
    }
    
    type StoreEnhancer<S, Ext = {}, StateExt = {}> = (
      next: StoreEnhancerStoreCreator<S>
    ) => StoreEnhancerStoreCreator<S, Ext, StateExt>
    
    type StoreEnhancerStoreCreator<S, Ext = {}, StateExt = {}> = (
      reducer: Reducer<S>,
      preloadedState?: DeepPartial<S>
    ) => Store<S> & Ext
    
    // My Code
    
    interface Config<S> {
      serialize: (state: S) => string;
    }
    
    const storage = <S>(config: Config<S>): StoreEnhancer<S> => createStore => (reducer, preloadedState) => {
      const { serialize } = config;
    
      const theStore = createStore(reducer, preloadedState);
    
      const state = theStore.getState();
      const serializedState = serialize(state);
    
      return theStore;
    }
    

    【讨论】:

    • 由于这需要更改 Redux 代码,这会带来什么样的向后兼容性风险?
    • @Shepmaster 我错过了那部分代码是 Redux 的一部分 .. 这可能不起作用..您可以尝试复制定义并修改它们.. 不确定更大的上下文是什么。
    【解决方案2】:

    问题在于storage 被声明为(间接地,通过StoreEnhancer)返回一个StoreEnhancerStoreCreator,这是一个必须适用于所有S 的通用函数。但是,对storage 的给定调用会生成一个仅适用于S 的商店创建者:已传递的config 中的S

    在我看来,生成StoreEnhancerStoreCreator 的唯一方法是从serialize 函数开始,该函数本身在S 中是通用的。我不确定这在您的场景中是否有意义。也许熟悉 Redux 的人会更好地了解该怎么做。

    【讨论】:

    • 一个 serialize 函数本身在 S 中是通用的 - 这似乎不太可能工作,因为那样我就不会知道状态的内容为了序列化它。
    【解决方案3】:

    Titian Cernicova-Dragomir's answer让我走上正轨:

    此解决方案需要在StoreEnhancerStoreCreatorStoreEnhancer 中添加额外的类型参数

    但是,适当的类型参数已经存在;我只是不明白如何正确使用它。具体来说,StoreEnhancer 返回带有默认类型参数的StoreEnhancerStoreCreator。如果我改为设置类型参数,我现在知道将返回哪种状态:

    const storage = <S>(config: Config<S>): StoreEnhancer =>
      (createStore: StoreEnhancerStoreCreator<{}, S>) =>
      //            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ set this type
        (reducer, preloadedState) => {
    

    【讨论】:

      猜你喜欢
      • 2015-10-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-11
      • 1970-01-01
      • 2019-07-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多