Sas Gabriel
Sas Gabriel

Reputation: 1556

How to have different return types based input object?

I have a method to filter a list based on some fileds and sometimes I can have an extra pageNumber and limit for which to filter my list and get only a limited number of entries.

This is what I have in code:

public getByFilter(
   fitler?: { [P in keyof T]?: T[P] } & { pageNumber?: number; limit?: number }
 ): Observable<IPagedResult<T>> { // code to filter }

and using this method

.getByFilter({ ...filters })
      .subscribe((response: IPagedResult<MyClass>) => {
        // do something
      });

Can my method have a return type of

Observable<T[]>

if my filters object does not contain pageNumber and limit?

and then to use it like

.getByFilter({ ...filters })
      .subscribe((response: MyClass[]) => {
        // do something
      });

Upvotes: 1

Views: 40

Answers (1)

DAG
DAG

Reputation: 6994

This is currently only possible with some hacky casting on the implementation side, the issue is being tracked however (#24929, #22735, #22596) and the maintainers declared it as a design limition.

The solution is to define the filter parameter as generic parameter like this:

type BasicFilter<T> = { [P in keyof T]?: T[P] };
type PaginationFilter<T> = BasicFilter<T> & { pageNumber: number, limit: number };
type Filter<T> = BasicFilter<T> | PaginationFilter<T>;

public getByFilter<F extends Filter<T>>(filter: F): F extends PaginationFilter<T> ? Observable<IPagedResult<T>> : Observable<T> {
    /**/
}

the return type is being infered based on whether input is a pagination filter or basic filter.

This works as expected on the call side:

const paged = new Observable<Test>({foo: 1, bar: ''}).getByFilter({
   pageNumber: 2,
   limit: 2,
});
// paged as type: Observable<IPagedResult<Test>>

const raw = new Observable<Test>({foo: 1, bar: ''}).getByFilter({});
// raw as type: Observable<Test>

On the implementation side you need a asseration as any like this (the mentioned issue which is being tracked on GitHub):

if ('pageNumber' in filter && 'limit' in filter) {
  return new Observable<IPagedResult<T>>({
     data: this.value,
  }) as any;
}
return this as any;

A full example can be found here in this playground.

Upvotes: 1

Related Questions