Is there a better way to define a conditional type?

Advertisements

I have a HATEOAS API that gives me structures based on my request. At the heart of it, it could be a single entity or an array of entities.

// single entity
{
    links: [{ ... }],
    data: {
        links: [{ ... }],
        data: {
           id: 1,
           title: "example entity"
        }
    }
}

// array of entities
{
    links: [{ ... }],
    data: [{
        links: [{ ... }],
        data: {
           id: 1,
           title: "example entity"
        }
    }, {
        links: [{ ... }],
        data: {
           id: 2,
           title: "another entity"
    }]
}

My generic types for all responses are as follows

type Link = {
    href: string;
    ref: string;
    type: string;
}

type EntityResponse<T> = { links: Link[], data: T };

type Response<T, Type extends [] | undefined> = {
    links: Link[];
    data: Type extends []
        ? EntityResponse<T>[]
        : EntityResponse<T>;
}

Ideally I would be able to type the data using the entities type

Response<Product>

// or

Response<Product[]>

But as each of the entities is interpolated with a links object it made it much trickier so I came up with the above.

So that if I wanted to define an array type response I would type it as follows
Response<Product, []>

For a single entity, I would type it
Response<Product>

I have a suspicion that I could remove the second type used conditionally but I’m unsure how I would achieve it given that I need to add the links key/value pair into each individual entity and I don’t want to add this to the entity type. Can anyone point me in the right direction?

>Solution :

If you use a conditional type that infers the potential array elements, you will be to able use Response<Product> and Response<Product[]> to define your response types:

type Response<T> = {
    links: Link[];
    data: T extends (infer E)[]
        ? EntityResponse<E>[]
        : EntityResponse<T>;
}

Playground link

Leave a ReplyCancel reply