Hvordan oppretter du sterkt typet Redux-mellomvare i TypeScript fra Reduxs typedefinisjoner?


Jeg har et TypeScript-prosjekt som bruker React og Redux, og jeg prøver å legge til noen mellomvarefunksjoner. Jeg startet ved å implementere en fra Redux sine eksempler som følger:

// ---- 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);

Ovenstående fungerer helt fint, men siden dette er TypeScript, ønsker jeg å gjøre det sterkt typet, helst ved å bruke typene definert av Redux slik at jeg ikke trenger å oppfinne og vedlikeholde mine egne. Så, her er de relevante utdragene fra min index.d.ts-fil for Redux:

// ---- 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>;
}

Jeg prøver å finne ut hvordan jeg kan få disse typene inn i Logger-metoden min, men jeg har ikke hatt stor suksess. Det virker for meg som om noe som dette burde fungere:

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);
    };
}

men i stedet får jeg denne feilen:

Feil TS2322: Type '(api: MiddlewareAPI) => (next: Dispatch) => (action: Action) => Action' kan ikke tilordnes typen 'Middleware'.
Typene til parameterne 'api' og 'api' er inkompatible.
Type 'MiddlewareAPI' kan ikke tilordnes typen 'MiddlewareAPI'.
Type 'S' kan ikke tilordnes typen 'MyStore'.

Jeg ser at <S> generisk er deklarert i typedefinisjonen, men jeg har prøvd mange forskjellige kombinasjoner, og jeg klarer ikke å finne ut hvordan jeg skal spesifisere det som MyStore slik at det blir gjenkjent som den generiske typen i resten av deklarasjonene. For eksempel, ifølge deklarasjonen skal api.getState() returnere et MyStore-objekt. Samme tankegang gjelder selvfølgelig for action-typen <A>.

Svar #1

Min Butikk er ikke påkrevd.

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

eller

export const Logger: Middleware = api => next => action => {
  // Do stuff
  return next(action);
};

Ha en fin utvikling

Svar #2

Her er min løsning:

Først er det middleware-skaperen som aksepterer en todo-funksjon som inndata, som kjøres som kjernelogikk for middleware. Todo-funksjonen aksepterer et objekt som kapsler inn store(MiddlewareAPI<S>), next(Dispatch<S>), action(Action<S>) samt eventuelle andre tilpassede parametere. Vær oppmerksom på at jeg bruker as Middleware for å tvinge middleware-skaperen til å returnere et Middleware. Dette er magien jeg bruker for å bli kvitt problemet.

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;
};

Den andre delen er definisjonen av min todo-funksjon. I dette eksemplet skriver jeg noen tokens inn i en cookie. Det er bare en POC for Middleware, så jeg bryr meg overhodet ikke om XSS-risikoen i kode min.

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);
};

Til slutt, her er hvordan oppsettet av butikken min ser ut.

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

Relaterte artikler