2

Typescript compiler keeps throwing error when I try to use union or multiple types / interfaces.

My requirement

From the server I receive an object as response in which one key ('errorMessages') has data type string[] and the other key ('data') can either be an object or an array. I wrote an interface for this which should look like below:

interface MyServerResponse {
    errorMessages: string[];
    data: {
        headers: string[];
        body: any[][];
    } | any[]>;
}

Received compiler error on trying to access 'headers'

Property 'headers' does not exist on type 'any[] | { headers: string[]; body: any[][]; }'

Isn't it possible to implement this using union just like for number | boolean, string | null, etc.?

2
  • Shouldn't the any[] be on body ? interface MyServerResponse { errorMessages: string[]; data: { headers: string[]; body: any[][]| any[]; } ; } Commented Jul 25, 2018 at 13:18
  • No, the body key within data is always any[][]. I want the union on data key (just as mentioned in the question). Updated the question for more clarity. Commented Jul 27, 2018 at 13:21

3 Answers 3

5

When you have a type union you can only access members that are common to all types. If you want to say it's one or the other you need a type guard to let the compiler know that it's of the right type:

// this type isn't needed but it makes the code more readable
type headersBody = { headers: string[]; body: any[][] }; 

function isHeadersBody(obj: headersBody | any[]): obj is headersBody {
    return !Array.isArray(obj);
    // or better yet
    // return Array.isArray(obj["headers"]) && Array.isArray(obj["body"]);    
}

let obj: headersBody | any[];

if (isHeadersBody(obj)) {
    console.log(obj.body.length);
}
Sign up to request clarification or add additional context in comments.

Comments

3

Typescript - Advanced Types says

If we have a value that has a union type, we can only access members that are common to all types in the union.

So you are getting the error because "headers" isn't common across the types in the union.

Comments

3

To inform TypeScript which of interfaces you expect you can just cast variable in this specific function:

interface dataTypeResponse {
    headers: string[];
    body: any[][];
}

interface MyServerResponse {
    errorMessages: string[];
    data: dataTypeResponse | any[];
}

doTheMagic(d: MyServerResponse) {
   // access it like this:
   const headers = (<dataTypeResponse>d.data).headers;
}

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.