我有以下界面:
interface AppState {
readonly grid : IGridSettings;
readonly selected : ISelectedSettings;
}
interface IGridSettings {
readonly extents : number;
readonly isXY : boolean;
readonly isXZ : boolean;
readonly isYZ : boolean;
readonly spacing : number;
}
interface ISelectedSettings {
readonly bodyColor : number;
readonly colorGlow : number;
readonly lineColor : number;
readonly selection : number[] | undefined;
}
interface Action<T = any> {
type: T
}
interface SetPropertyValueAction<KAction extends keyof AppState, KActionProp extends keyof AppState[KAction]> extends Action {
type : string;
payload : {
property : [KAction, KActionProp];
value : IUserActions[KAction][KActionProp];
};
}
最终,当setPropertyValue函数发出时,我的redux减速器被调用:
const setPropertyValue = <KAction extends keyof AppState, KActionProp extends keyof AppState[KAction]>( property : [KAction, KActionProp], value : AppState[KAction][KActionProp] ) : SetPropertyValueAction<KAction, KActionProp> => ( {
type: 'SET_PROPERTY_VALUE',
payload: {
property,
value,
}
} );
function myReducer( state : AppState = INITIAL_STATE, a : Action ) : AppState {
switch( a.type ) {
case 'SET_PROPERTY_VALUE': {
const action = a as SetPropertyValueAction; // <- error here
return createNewState( state, action.payload.property, action.payload.value );
}
}
return states;
}
我遇到的问题是我不知道如何将Action的演员表固定为SetPropertyValueAction。 TypeScript告诉我错误是:
Generic type 'SetPropertyValueAction<KAction, KActionProp>' requires 2 type argument(s).
我尝试将有错误的代码行更改为以下内容:
const action = a as SetPropertyValueAction<KAction extends keyof AppState, KActionProp extends keyof AppState[KAction]>;
...但是这给了我两个错误,说“找不到名称KAction和KActionProp”
如何将Action对象转换为通用SetPropertyValueAction?
这是我在此处已回答的其他问题的后续问题:How to add TypeScript type safety to my redux action?
答案 0 :(得分:1)
我们需要为类型指定类型参数。由于我们并不真正了解它们,因此我们需要与keyof T
兼容但代表的不是T
的实际键。 unknown
不会执行,因为它不满足约束,我们可以使用的一种类型是any
或never
(它是任何类型的子类型)
interface AppState {
readonly grid: IGridSettings;
readonly selected: ISelectedSettings;
}
interface IGridSettings {
readonly extents: number;
readonly isXY: boolean;
readonly isXZ: boolean;
readonly isYZ: boolean;
readonly spacing: number;
}
interface ISelectedSettings {
readonly bodyColor: number;
readonly colorGlow: number;
readonly lineColor: number;
readonly selection: number[] | undefined;
}
interface Action<T = any> {
type: T
}
interface SetPropertyValueAction<KAction extends keyof AppState, KActionProp extends keyof AppState[KAction]> extends Action {
type: string;
payload: {
property: [KAction, KActionProp];
value: AppState[KAction][KActionProp];
};
}
const setPropertyValue = <KAction extends keyof AppState, KActionProp extends keyof AppState[KAction]>(property: [KAction, KActionProp], value: AppState[KAction][KActionProp]): SetPropertyValueAction<KAction, KActionProp> => ({
type: 'SET_PROPERTY_VALUE',
payload: {
property,
value,
}
});
declare const INITIAL_STATE: AppState;
// implementation if necessary
function createNewState<KAction extends keyof AppState, KActionProp extends keyof AppState[KAction]>(state: AppState, property: [KAction, KActionProp], value: AppState[KAction][KActionProp]): AppState {
return Object.assign({ ...state }, {
[property[0]]: Object.assign({ ...state[property[0]] }, {
[property[1]]: value,
})
});
}
function myReducer(state: AppState = INITIAL_STATE, a: Action): AppState {
switch (a.type) {
case 'SET_PROPERTY_VALUE': {
const action = a as SetPropertyValueAction<never, never>; // we don't know the actual type, never will have to do
return createNewState(state, action.payload.property, action.payload.value);
}
}
return state;
}