我有以下动作创作者:
/** simply captures the string literal type */
function actionType<T extends string, U>(type: T, u: U) {
return Object.assign({}, { type }, u);
}
// ------------------------------------
// Action Creators
// ------------------------------------
const ACTION_CREATORS = {
toggleFilter: (params: { filterValueKey: string; filterKey: string; filterValue: string }) => {
const { filterKey, filterValue, filterValueKey } = params;
return actionType('FILTERS_TOGGLE', {
payload: {
filterValueKey,
filterKey,
filterValue
}
});
},
deleteFilter: (params: { filterValueKey: string }) => {
const { filterValueKey } = params;
return actionType('FILTERS_DELETE', {
payload: {
filterValueKey
}
});
},
searchFilterValues: (params: { activeFilterKey: string; query: string }) => {
const { query, activeFilterKey } = params;
return actionType('FILTERS_QUERY_VALUES', {
payload: {
query,
activeFilterKey
}
});
}
};
我想写的是
type ActionHandlers<T extends {
[actionCreator: string]: (...args: any[]) => { type: string }
}> = { /* ... */ };
将为&#34;动作处理程序&#34;添加正确的输入。对象如下:
const ACTION_HANDLERS: ActionHandlers<typeof ACTION_CREATORS > = {
FILTERS_TOGGLE: (state, action) => {
action.payload.filterValueKey // this key is typed
const newState = { ...state };
return newState;
},
FILTERS_DELETE: () => {} /* ... */,
FILTERS_QUERY_VALUES: (state, action) => {
action.payload.query // this is also typed and typed differently
const newState = { ...state };
return newState;
}
}
到目前为止,我能够捕获动作类型的名称并抓取动作创建者的返回类型,但我仍然坚持这一点:
type Action<T extends { type: string }> = T;
type ActionCreator<T extends string> = (...params: any[]) => { type: T };
type MapToNames<T extends { [key: string]: ActionCreator<any> }> = {
[P in keyof T]: T[P] extends ActionCreator<infer U> ? U : never
};
type ActionNames<T extends { [key: string]: ActionCreator<any> }> = MapToNames<
T
>[keyof MapToNames<T>];
type RemoveFunctions<T extends { [key: string]: (...args: any[]) => { type: string } }> = {
[P in keyof T]: T[P] extends (...params: any[]) => Action<infer U> ? U : never
};
type Names = ActionNames<typeof ACTION_CREATORS>;
// type Names = "FILTERS_TOGGLE" | "FILTERS_DELETE" | "FILTERS_QUERY_VALUES"
type NoFunctions = RemoveFunctions<typeof ACTION_CREATORS>;
任何想法都将不胜感激!
答案 0 :(得分:1)
这是一种可能的解决方案:
type GetActionCreatorTypes<T extends { [key: string]: (...args: any[]) => { type: string } }> = {
// Get the return type of the action creator
[P in keyof T]: T[P] extends (...params: any[]) => Action<infer U> ? U : never
}[keyof T]; // Make a union of all action types
type ActionHandlers<T extends {
[actionCreator: string]: (...args: any[]) => { type: string }
}> = {
// Take all properties named type (will be a union of "FILTERS_TOGGLE" | "FILTERS_DELETE" | "FILTERS_QUERY_VALUES" in our case)
[P in GetActionCreatorTypes<T>['type']]: (
state: any,
// Type the action as beeing the only type in the union with the type property named P
action: Extract<GetActionCreatorTypes<T>, { type: P }>
) => any
};
const ACTION_HANDLERS: ActionHandlers<typeof ACTION_CREATORS> = {
FILTERS_TOGGLE: (state, action) => {
action.payload.filterValueKey // ok
const newState = { ...state };
return newState;
},
FILTERS_DELETE: () => { } /* ... */,
FILTERS_QUERY_VALUES: (state, action) => {
action.payload.query // this is also ok
const newState = { ...state };
return newState;
}
}
游乐场link