TypeScript:如何转换一般对象?

时间:2019-01-05 02:05:45

标签: typescript redux casting type-safety

我有以下界面:

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?

1 个答案:

答案 0 :(得分:1)

我们需要为类型指定类型参数。由于我们并不真正了解它们,因此我们需要与keyof T兼容但代表的不是T的实际键。 unknown不会执行,因为它不满足约束,我们可以使用的一种类型是anynever(它是任何类型的子类型)

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;
}