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