将枚举映射到Typescript中的子类类型

时间:2018-03-23 19:27:39

标签: typescript enums redux

我使用的是Typescript和Redux。有没有办法确保根据action键入减速器的type参数?这类似于实体框架将枚举列映射到适当的子类。

这是一个不完整的例子:

enum ActionType {
  doThing1,
  doThing2      
}

interface Action {
  readonly type: ActionType
}

interface Thing1Action extends Action {
   readonly payload: Thing1Payload;
}

interface Thing2Action extends Action {
   readonly payload: Thing2Payload;
}


interface State {
   readonly thing1: Thing1Payload;
   readonly thing2: Think2Payload;
}

const initialState: State = {
   thing1: null,
   thing2: null,
}

function reducer(state = initialState, action: Action): State {
    switch (action.type)
    {
        case ActionType.doThing1: 
            return {...state, thing1: action.payload };
    }
    return state;
}

在上面的示例中,我想约束action.payload来代表Thing1Payload。现在,Typescript会抱怨action没有payload,或者当我省略action的类型时,它会为我提供any

我只是在代码和编译时检查它。

1 个答案:

答案 0 :(得分:3)

是的,这称为tagged union。在继承自Action的每个接口中,您必须重新定义type字段,但是使用适当的枚举文字类型键入它(基本上将单个可能的枚举值指定为字段的类型)。

然后使用所有派生接口定义联合类型,并将其用作参数类型。然后切换type将充当类型保护。

enum ActionType {
    doThing1,
    doThing2
}

interface Action {
    readonly type: ActionType
}
interface Thing1Payload {
    b: number
}

interface Thing2Payload {
    a: number
}
interface Thing1Action extends Action {
    readonly type: ActionType.doThing1 // redefine the filed, only value assignable is ActionType.doThing1
    readonly payload: Thing1Payload;
}

interface Thing2Action extends Action {
    readonly type: ActionType.doThing2 // redefine the filed, only value assignable is ActionType.doThing2
    readonly payload: Thing2Payload;
}


interface State {
    readonly thing1: Thing1Payload;
    readonly thing2: Thing2Payload;
}

const initialState: State = {
    thing1: <Thing1Payload>null,
    thing2: <Thing2Payload>null,
}
// Interface with all actions
type AllAction = Thing1Action | Thing2Action;

function reducer(state = initialState, action: AllAction): State {
     // This switch will now be a type guard
    switch (action.type) {
        case ActionType.doThing1:
            // action will be of typed Thing1Action
            return { ...state, thing1: action.payload };
        case ActionType.doThing2:
            // action will be of typed Thing2Action
            return { ...state, thing2: action.payload };
    }
    return state;
}