Typesafe multiple return types with Typescript

Typesafe multiple return types with Typescript

Typescipt types and runtime values are two separate levels, basically writing typescript conditions based on values is not supported, but there is a solution.

What I would like to achieve is the following: A function return type changes based on the value entered in the function parameter.

function getUsers(withPagination: boolean): IUser[] | IPaginatedUsers { 
  // ... 
  return users; 
}

The purpose of this function is to return users from an api, if withPagination is true then it wraps the user array in pagination, if false then only the user array is returned (just an example).

The problem is that runtime reveals the return value based on the "withPagination" variable the return value is not specific, but we know what it will be, it can cause some headache.

One solution would be to specify the return value generically, but that is not very convenient and can be messed up. The other solution is to create two separate functions, which in this case might be better but it can cause code duplication.

function getUsers(): IUser[] { 
  // ... 
  return users; 
}

function getUserswithPagination(): IPaginatedUsers { 
  // ... 
  return users; 
}

If we want to keep it together and implement 2 or more different returns in a typesafe way, then you can do it this way:

interface IUser {
    name: string,
    age: number,
}

interface IPaginated<T> {
    data: T[],
    meta: {
        page: number,
        limit: number,
        totalPages: number
    }
}

interface IResponseOptions {
    withPagination: boolean
}

const DEFAULT_RESPONSE_OPTION = {
    withPagination: true
}

type UserResultType<T extends IResponseOptions> = T extends {
    withPagination: true
} ? IPaginated<IUser> : IUser[];


function getUsers<T extends IResponseOptions>(withPagination: T = DEFAULT_RESPONSE_OPTION as any): UserResultType<T> { 
  // fetch data..
  return "something" as any as UserResultType<T>; 
}



getUsers({withPagination: true}); // return IPaginated<IUser>
getUsers({withPagination: false}); // return IUser[];
getUsers(); // returns bsed on DEFAULT_RESPONSE_OPTION: IPaginated<IUser>