问题
I have the following interfaces:
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];
};
}
Eventually my redux reducer gets called when the setPropertyValue function is emitted:
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;
}
The problem that I'm having is I don't know how to fix the cast from Action to SetPropertyValueAction. TypeScript tells me the error is:
Generic type 'SetPropertyValueAction<KAction, KActionProp>' requires 2 type argument(s).
I tried changing the line of code that has the error to the following:
const action = a as SetPropertyValueAction<KAction extends keyof AppState, KActionProp extends keyof AppState[KAction]>;
...but this gives me two errors saying 'Cannot find name KAction and KActionProp'
How can I cast an Action object to the generic SetPropertyValueAction ??
This is a follow up question to my other question that has been answered here: How to add TypeScript type safety to my redux action?
回答1:
We need to specify the type parameters for the type. Since we don't really know them, we need something that is compatible with keyof T
but represent is not an actual key of T
. unknown
will not do since it does not satisfy the constraint, the one type we can use is any
or never
(which is a subtype of any type)
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;
}
来源:https://stackoverflow.com/questions/54048358/typescript-how-to-cast-a-generic-object