0

I'm using nrgx/store and following the example app I'm using classes to strongly type my actions like

export const ActionTypes ={
    SET_ACTIVE_USER: type('[UserState] Set Active User'),
    SET_USERLIST: type('[UserState] Set Userlist')
};

export class SetActiveUserAction implements Action {
    type = ActionTypes.SET_ACTIVE_USER;
    constructor(public payload:User){}
}

export class SetUserlistAction implements Action {
    type = ActionTypes.SET_USERLIST;
    constructor(public payload:User[]){}
}

export type Actions
    = SetActiveUserAction
    | SetUserlistAction

I'm having now a type problem in my reducer. Since the exported Actions can be of type User or User[], it doesn't let me assign userlist:User[] to it (i want to use array methods like forEach)

export function reducer(state = INITIAL_STATE, action: userAction.Actions): State {

    switch (action.type) {
        case userAction.ActionTypes.SET_USERLIST: {

            let userlist:User[] = action.payload <-------Type Issue

            return tassign(state, { userlist: action.payload })
        }

        default: {
            return state
        }
    }
}
1

1 Answer 1

2

One way to handle this is to cast the property so TypeScript knows what type it should be.

let userlist:User[] = <User[]>action.payload;

If that doesn't work then you can cast to any and then to the type.

let userlist:User[] = <User[]><any>action.payload;

But that's not really using the type system to it's full potential, and isn't very nice code.

Rather than forcing the type to be a specific type using a cast, it'd be nice if we could tell the type system how to figure out what type it is.

So ... it sounds like you want Type Guards.

https://www.typescriptlang.org/docs/handbook/advanced-types.html
(Look for the section titled 'User-Defined Type Guards')

You can make a special function that tests if an object is of a certain type.
That function returns a bool, true for a match, false for no match.
When you test a variable using that function in your code TypeScript recognises the type test and changes the type of that variable to match the tested type.

So you end up with code like this:

if (isUserAction(action)) {
    let user: IUser = action.Payload;
}

I couldn't get them working with the exact object layout you have, but I made a similar example with a few tweaks that allow the type guards to work.

enum ActionType { User = 1, Users = 2 }
interface IAction { Type: ActionType; }
interface IUser { Name: string; }

class Action<T> implements IAction {
    public Type: ActionType;
    public Payload: T;

    constructor(type: ActionType, payload: T) {
        this.Type = type;
        this.Payload = payload;
    }
}

class UserAction extends Action<IUser> {
    constructor(payload?: IUser) {
        super(ActionType.User, payload);
    }
}

class UsersAction extends Action<IUser[]> {
    constructor(payload?: IUser[]) {
        super(ActionType.Users, payload);
    }
}

function isUserAction(action: IAction): action is UserAction {
    return action.Type === ActionType.User;
}

function isUsersAction(action: IAction): action is UsersAction {
    return action.Type === ActionType.Users;
}

function processAction(action: IAction) {
    if (isUserAction(action)) {
        let user: IUser = action.Payload;
        console.log(`user: ${user.Name}`);
    } else if (isUsersAction(action)) {
        let users: IUser[] = action.Payload;
        for (let user of users) {
            console.log(`users: ${user.Name}`);
        }
    } 
}

processAction(new UserAction({ Name: "foo" }));
processAction(new UsersAction([{ Name: "bar" }, { Name: "baz" }]));

Code @ TypeScript Playground

Output:

user: foo  
users: bar  
users: baz

The documentation is very good and has lots of detail to teach you about this features, and all the rest.

Another good source is basarat.gitbooks.io.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.