我有一个使用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);
上面的工作正常,但由于这是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>;
}
我正在试图弄清楚如何将这些类型带入我的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);
};
}
但我得到了这个错误:
错误TS2322:输入'(api:MiddlewareAPI)=&gt; (下一篇:Dispatch)=&gt; (行动:行动)=&gt; “行动”不能分配给“中间件”类型 参数“api”和“api”的类型不兼容 类型'MiddlewareAPI'不能分配给'MiddlewareAPI'类型 类型“S”不能指定为“MyStore”类型。
我看到&lt; S&gt;在类型定义中声明的泛型,但我尝试了很多不同的组合,我似乎无法弄清楚如何将其指定为MyStore,以便在其余的声明中将其识别为泛型类型。例如,根据声明api.getState()应该返回一个MyStore对象。当然,同样的想法也适用于行动类型&lt; A&gt ;.
答案 0 :(得分:8)
不需要MyStore。
var propertyname = 'house';
var content = document.createElement('div');
var img = document.createElement('img');
if (propertyname == 'house') {
img.src = 'https://placehold.it/300x100';
}
content.appendChild(img);
document.getElementsByTagName('body')[0].appendChild(content);
或
export const Logger: Middleware =
(api: MiddlewareAPI<void>) =>
(next: Dispatch<void>) =>
<A extends Action>(action: A) => {
// Do stuff
return next(action);
};
有一个好的开发
答案 1 :(得分:2)
我有一个类似的解决方案:
export type StateType = { thing: string, item: number };
export type ActionType =
{ type: "MY_ACTION", note: string } |
{ type: "PUSH_ACTIVITIY", activity: string };
// Force cast of generic S to my StateType
// tslint:disable-next-line:no-any
function isApi<M>(m: any): m is MiddlewareAPI<StateType> {
return true;
}
export type MiddlewareFunction =
(api: MiddlewareAPI<StateType>, next: (action: ActionType) => ActionType, action: ActionType) => ActionType;
export function handleAction(f: MiddlewareFunction): Middleware {
return <S>(api: MiddlewareAPI<S>) => next => action => {
if (isApi(api)) {
// Force cast of generic A to my ActionType
const _action = (<ActionType>action);
const _next: (action: ActionType) => ActionType = a => {
// Force cast my ActionType to generic A
// tslint:disable-next-line:no-any
return next(<any>a);
};
// Force cast my ActionType to generic A
// tslint:disable-next-line:no-any
return f(api, _next, _action) as any;
} else {
return next(action);
}
};
}
使用handeAction
函数,我现在可以定义中间件:
// Log actions and state.thing before and after action dispatching
export function loggingMiddleware(): Middleware {
return handleAction((api, next, action) => {
console.log(" \nBEGIN ACTION DISPATCHING:");
console.log(`----- Action: ${JSON.stringify(action)}\n`);
const oldState = api.getState();
const retVal = next(action);
console.log(` \n----- Old thing: ${oldState.thing}`);
console.log(`----- New thing: ${api.getState().thing)}\n`);
console.log("END ACTION DISPATCHING\n");
return retVal;
});
}
// Another middleware...
export interface DataHub = { ... }:
export function dataHandlingMiddleware(datahub: DataHub): Middleware {
return handleAction((api, next, action) => {
switch (action.type) {
case "PUSH_ACTIVITY": {
handlePushActivities(action.activity, api, /* outer parameter */ datahub);
break;
}
default:
}
return next(action);
});
}
请注意,中间件还可能需要在安装过程中传入的其他参数,如服务等(此处为:DataHub)。 商店设置如下所示:
import {
Store, applyMiddleware, StoreCreator, StoreEnhancer,
createStore, combineReducers, Middleware, MiddlewareAPI
} from "redux";
const middlewares = [
dataHandlingMiddleware(datahub),
loggingMiddleware()];
const rootReducer = combineReducers<StateType>({ ... });
const initialState: StateType = {};
// Trick to enable Redux DevTools with TS: see https://www.npmjs.com/package/redux-ts
const devTool = (f: StoreCreator) => {
// tslint:disable-next-line:no-any
return ((window as any).__REDUX_DEVTOOLS_EXTENSION__) ? (window as any).__REDUX_DEVTOOLS_EXTENSION__ : f;
};
const middleware: StoreEnhancer<StateType> = applyMiddleware(...middlewares);
const store: Store<StateType> = middleware(devTool(createStore))(rootReducer, initialState);
希望这有帮助。
答案 2 :(得分:2)
这是我的解决方案:
首先是中间件创建者接受todo函数作为输入,它作为中间件的核心逻辑运行。 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;
};
第二部分是我的待办事项功能的定义。在这个例子中,我将一些令牌写入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);
};
最后,这是我的商店配置的样子。
const store: Store<{}> = createStore(
rootReducer,
// applyMiddleware(thunk, oauth2TokenMiddleware(fetch))
applyMiddleware(thunk, createMiddleware<OAUTH2Token>(saveToken2Cookie))
);
答案 3 :(得分:0)
我刚刚遇到了和你一样的问题!
通过在括号之间放置最后一个函数然后强制它的类型为Dispatch<EffectAction>
来解决它
interface EffectAction extends Action {
effect<T> (action: T): void
}
const effects: Middleware = (api: MiddlewareAPI<any>) => (next: Dispatch<EffectAction>) => ((action: EffectAction) => {
if (action.effect instanceof Function) action.effect(action)
return next(action)
}) as Dispatch<EffectAction>
答案 4 :(得分:0)
这是一种中间件类型,使您不必注释已管理的函数:
import { Dispatch, MiddlewareAPI, AnyAction } from 'redux'
import { MyStore, MyEvent } from 'src/store'
type Middleware<S, E extends AnyAction> =
(api: Dispatch<E> extends Dispatch<AnyAction> ? MiddlewareAPI<Dispatch<E>, S> : never) =>
(next: Dispatch<E>) =>
(event: E) => ReturnType<Dispatch<E>>
const middleware: Middleware<MyStore, MyEvent> = (api) => (next) => (event) => {
// ...
}