如何从Redux的类型定义中在TypeScript中创建强类型的redux中间件?

Ber*_*men 19 typescript redux

我有一个使用React和Redux的TypeScript项目,我正在尝试添加一些中间件函数.我开始实现Redux的样本中的一个样本:

// ---- middleware.ts ----
export type MiddlewareFunction = (store: any) => (next: any) => (action: any) => any;

export class MyMiddleWare {
    public static Logger: MiddlewareFunction = store => next => action => {
        // Do stuff
        return next(action);
    }
}

// ---- main.ts ---- 
import * as MyMiddleware from "./middleware";

const createStoreWithMiddleware = Redux.applyMiddleware(MyMiddleWare.Logger)(Redux.createStore);
Run Code Online (Sandbox Code Playgroud)

上面的工作正常,但由于这是TypeScript,我想使它强类型,理想情况下使用Redux定义的类型,所以我不必重新发明和维护自己的类型.因此,以下是我的Redux的index.d.ts文件的相关摘录:

// ---- index.d.ts from Redux ----
export interface Action {
    type: any;
}

export interface Dispatch<S> {
    <A extends Action>(action: A): A;
}

export interface MiddlewareAPI<S> {
    dispatch: Dispatch<S>;
    getState(): S;
}

export interface Middleware {
    <S>(api: MiddlewareAPI<S>): (next: Dispatch<S>) => Dispatch<S>;
}
Run Code Online (Sandbox Code Playgroud)

我试图找出如何将这些类型带入我的Logger方法,但我没有太多运气.在我看来,这样的事情应该有效:

interface MyStore {
    thing: string;
    item: number;
}

interface MyAction extends Action {
    note: string;
}

export class MyMiddleWare {
    public static Logger: Middleware = (api: MiddlewareAPI<MyStore>) => (next: Dispatch<MyStore>) => (action: MyAction) => {
        const currentState: MyStore = api.getState();
        const newNote: string = action.note;
        // Do stuff
        return next(action);
    };
}
Run Code Online (Sandbox Code Playgroud)

但我得到这个错误:

错误TS2322:输入'(api:MiddlewareAPI)=>(next:Dispatch)=>(action:Action)=> Action'不能分配给'Middleware'类型.
参数"api"和"api"的类型不兼容.
类型'MiddlewareAPI'不能分配给'MiddlewareAPI'类型.
类型"S"不能分配给"MyStore"类型.

我看到在类型定义中声明的<S>泛型,但我尝试了很多不同的组合,我似乎无法弄清楚如何将其指定为MyStore,以便在其余部分中将其识别为泛型类型声明.例如,根据声明api.getState()应该返回一个MyStore对象.当然,同样的想法也适用于动作类型<A>.

小智 10

MyStore不是必需的.

export const Logger: Middleware =
  (api: MiddlewareAPI<void>) => 
  (next: Dispatch<void>) => 
  <A extends Action>(action: A) => {
    // Do stuff
   return next(action);
  };
Run Code Online (Sandbox Code Playgroud)

要么

export const Logger: Middleware = api => next => action => {
  // Do stuff
  return next(action);
};
Run Code Online (Sandbox Code Playgroud)

有一个漂亮的开发

  • @NSjonas-很抱歉不及早回复。我的一位同事指出,类型定义使得在结构上无法直接创建强类型中间件。您可以做的最好的事情就是围绕Martin Backschat的建议实施某种工作。这实际上是我最终要做的,尽管当时我还不知道使用“ is”创建类型保护,所以我的解决方案不如Martin的好。FWIW:看起来人们一直在解决这个问题(https://github.com/reactjs/redux/pull/2563),但是我还没有尝试过这些更改。 (2认同)

小智 6

这是我的解决方案:

首先是接受 todo 函数作为输入的中间件创建者,该函数作为中间件的核心逻辑运行。待办函数接受一个对象,封装store(MiddlewareAPI<S>)next(Dispatch<S>)action(Action<S>)以及其他任何你custimized参数。请注意,我as Middleware用来强制中间件创建者返回一个中间件。这是我用来摆脱麻烦的魔法。

import { MiddlewareAPI, Dispatch, Middleware } from 'redux';
import { Action } from 'redux-actions';

export interface MiddlewareTodoParams<S> {
  store: MiddlewareAPI<S>;
  next: Dispatch<S>;
  action: Action<S>;
  [otherProperty: string]: {};
}

export interface MiddlewareTodo<S> {
  (params: MiddlewareTodoParams<S>): Action<S>;
}

// <S>(api: MiddlewareAPI<S>): (next: Dispatch<S>) => Dispatch<S>;
export const createMiddleware = <S>(
  todo: MiddlewareTodo<S>,
  ...args: {}[]
): Middleware => {
  return ((store: MiddlewareAPI<S>) => {
    return (next: Dispatch<S>) => {
      return action => {
        console.log(store.getState(), action.type);
        return todo({ store, next, action, ...args });
      };
    };
  // Use as Middleware to force the result to be Middleware
  }) as Middleware;
};
Run Code Online (Sandbox Code Playgroud)

第二部分是我的 todo 函数的定义。在这个例子中,我将一些令牌写入 cookie。它只是中间件的 POC,所以我根本不关心代码中的 XSS 风险。

export type OAUTH2Token = {
  header: {
    alg: string;
    typ: string;
  };
  payload?: {
    sub: string;
    name: string;
    admin: boolean;
  };
};


export const saveToken2Cookie: MiddlewareTodo<OAUTH2Token> = params => {
  const { action, next } = params;
  if (action.type === AUTH_UPDATE_COOKIE && action.payload !== undefined) {
    cookie_set('token', JSON.stringify(action.payload));
  }
  return next(action);
};
Run Code Online (Sandbox Code Playgroud)

最后,这是我的商店配置的外观。

const store: Store<{}> = createStore(
  rootReducer,
  // applyMiddleware(thunk, oauth2TokenMiddleware(fetch))
  applyMiddleware(thunk, createMiddleware<OAUTH2Token>(saveToken2Cookie))
);
Run Code Online (Sandbox Code Playgroud)