Typescript泛型函数重载
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Typescript泛型函数重载相关的知识,希望对你有一定的参考价值。
我试图添加函数参数重载从最具体的开始,但类型缩小似乎不起作用。还尝试将参数更改为联合类型,但类型保护也不起作用。我错过了什么?
type IReducer<S, A> = (state: S, action: A) => S;
interface IAsyncHandlers<S, A extends IAction> {
request?: IReducer<S, A>;
success?: IReducer<S, A>;
failure?: IReducer<S, A & { payload: any; error: true }>;
}
interface IAction {
type: string;
payload: any;
}
const getActionHandler = <S, A>(handler?: IReducer<S, A>) => (state: S) => (action: A): S =>
handler ? handler(state, action) : state;
const handleAsyncAction = <S, A extends IAction>(handlers: IAsyncHandlers<S, A>): IReducer<S, A> => {
function reducer(state: S, action: A): S
function reducer(state: S, action: A & { error: true }): S;
function reducer(state: S, action: A & { meta: { isPending: true } }): S;
function reducer(state: S, action: A & { error?: any; meta?: any } ): S {
switch (true) {
case action.error:
// Property 'error' is optional in type 'IAction & { error?: any; meta?: any; }'
// but required in type '{ payload: any; error: true; }'.
return getActionHandler(handlers.failure)(state)(action);
case action.meta && action.meta.isPending:
return getActionHandler(handlers.request)(state)(action);
default:
return getActionHandler(handlers.success)(state)(action);
}
}
return reducer;
};
这里有很多事情要做。
首先,你真的不需要重载:这些签名的唯一区别是action
的类型。当多个签名以某种协调的方式不同时,过载是有用的;例如,如果函数的返回值的类型取决于action
的类型,或者state
参数类型取决于action
的类型。由于签名中的其他任何东西都不依赖于action
的类型,你可以从调用者那里获得相同的行为(正如你所尝试的)将action
更改为union类型(基本上只是A
,因为A | (A & B) | (A & C) | (A & D)
是essentially equivalent到A
。 )
顺便说一下,你的重载实际上是从最少到最具体的顺序排序,这是你通常想要的倒退。呼叫签名按从上到下的顺序进行检查。如果一个电话与第一个签名reducer(state: S, action: A): S
不匹配,它绝对不会匹配任何后续签名reducer(state: S, action: A & XYZ): S
。这意味着只有第一个签名才能在实践中使用。如果这里需要重载,我会告诉你先把更具体的内容放在首位,然后提供更多有关“具体”内容的细节。但这并不重要,因为您不需要重载。
您的问题实际上在函数的实现中,您尝试在type guard变量的类型上使用switch语句作为action
。不幸的是,action
的类型涉及A
,一个通用的类型参数,和TypeScript does not do narrowing on generic parameters。它一直是requested,但显然这样的缩小会导致编译器出现严重的性能问题。我的建议是使用user-defined type guards对发生的变窄进行更多控制。它有点冗长但应该有效:
const isErrorAction =
<A extends IAction & { error?: any }>(a: A): a is A & { error: true } =>
(a.error)
const isRequestAction =
<A extends IAction & { meta?: { isPending?: any } }>(
a: A
): a is A & { meta: { isPending: true } } =>
(a.meta && a.meta.isPending);
const handleAsyncAction = <S, A extends IAction>(
handlers: IAsyncHandlers<S, A>
): IReducer<S, A> => {
function reducer(state: S, action: A): S {
if (isErrorAction(action)) {
return getActionHandler(handlers.failure)(state)(action);
}
// not strictly necessary to narrow here, but why not
if (isRequestAction(action)) {
return getActionHandler(handlers.request)(state)(action);
}
return getActionHandler(handlers.success)(state)(action);
}
return reducer;
};
现在,实现类型检查没有错误,并且呼叫签名已简化为单个。希望有所帮助。祝好运!
以上是关于Typescript泛型函数重载的主要内容,如果未能解决你的问题,请参考以下文章