1

I'm receiving the following in an API response:

{ "roles": [ "ADMIN", "USER" ] }

where the response will always contain an array of roles (USER, PRESENTER, ORGANIZER, and ADMIN).

I want to convert it into a valid TypeScript array (Role[]), where the type Role is defined as follows:

export type Role = 'USER' | 'PRESENTER' | 'ORGANIZER' | 'ADMIN'

Any ideas?

9
  • 1
    from where presenter & organizer came into picture? ALso the expected output is not clear Commented Apr 25, 2018 at 16:13
  • If you are looking for speedup using Symbols would be a good idea Commented Apr 25, 2018 at 16:16
  • No, I just want to parse the string received into a valid enum. Commented Apr 25, 2018 at 16:17
  • 1
    Are you just looking to cast the response type to be a Role[]? Is there an issue with using the usual cast using as? EG, something like const roles = response.roles as Role[]? Commented Apr 25, 2018 at 16:21
  • 1
    Is there a reason you can't just put an assertion in there to let TypeScript know about the type? i.e. as Role[] Commented Apr 25, 2018 at 16:21

3 Answers 3

3

Your Role type is not an enum. It is just a string type limited to certain values.

You can just cast the result as a Role[] and TypeScript will be happy. This assumes the incoming data never has a bad value!

const data: {roles: Role[]} = JSON.parse('{"roles": ["ADMIN", "USER"]}');
data.roles // TypeScript knows it is a Role[]
Sign up to request clarification or add additional context in comments.

4 Comments

And if data might have bad input, just run an if and you're done :)
@Victor: But coding that if in an extensible way is tricky.
@T.J.Crowder but there's no other way than having a model to validate inputs from an API. Another way is using a JSON Schema which will handle the ifs for you
@Victor: Not sure what you mean by "no other way" given my answer.
1

You can just cast it to your union type:

const apiRoleArray = ["ADMIN", "USER"];
const realRoleArray: Role[] = <Role[]>apiRoleArray;

BUT you probably want to validate its contents rather than just trusting the API. :-) Drawing on this question's answers, you can create the type by using the keys of an object rather than defining it literally (see the accepted answer there for why):

const roleStrings = {
    USER: "",
    PRESENTER: "",
    ORGANIZER: "",
    ADMIN: ""
};

export type Role = keyof typeof roleStrings;

then give yourself a validation function:

const isRole = (s: string): s is Role => {
    return roleStrings.hasOwnProperty(s);
};

then a robust conversion function, for example:

const rawToRoleArray = (rawArray: string[]): Role[] => {
    return rawArray.map(s => {
        if (!isRole(s)) {
            throw new Error("Invalid Role: " + s);
        }
        return <Role>s;
    });
};

(you could combine those if you don't need them separately)

then use it:

// Valid
const realRoleArray: Role[] = rawToRoleArray(["ADMIN", "USER"]); 
console.log(realRoleArray);
// Invalid
const realRoleArray2: Role[] = rawToRoleArray(["ADMIN", "FOO"]); 
console.log(realRoleArray2);

Live in the playground | Live on jsFiddle

3 Comments

Here's the if I was talking about: if (!isRole(s)).
I appreciate that, yeah. It is important!
this is definitely the better answer if you do not trust the values coming from the API, or want to code defensively against future changes to the API contract.
0

If I got you corectly thats what you want to do.

enum RoleEnum {
  USER,
  PRESENTER,
  ORGANIZER,
  ADMIN
}

const parseEnum = (name: String): RoleEnum  => RoleEnum[`${name}`]

const parsed: RoleEnum[] = [ 'ADMIN', 'USER' ].map(parseEnum)

console.log(parsed)

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.