Shift 'n Tab
Shift 'n Tab

Reputation: 9453

Function overloading with interface argument

I'm trying to make a function overloading for the search() function. This method must have different search actions.

  1. search with only string type.
    • search(key: string): IPagination<Employee[]>;
  2. search with BasicFilter type:
    • search(x: BasicFilter): IPagination<Employee[]>;
  3. search with PaginatedFilter type:
    • search(y: PaginatedFilter): IPagination<Employee[]>;

How can i check the type of this interfaces in search(args: any): any; method?

Tried doing the following:

  if (typeof args === BasicFilter) {
    console.log('searched with basic filter arguments');
  }

TS message: 'BasicFilter' only refers to a type, but is being used as a value here.

Error message: BasicFilter is not defined


Here are the following codes:

The Interfaces

interface PageData {
  pageIndex: number;
  pageSize: number;
}

interface Employee {
  id: number;
  name: string;
}

interface BasicFilter {
  key: string;
  is_archived: boolean;
}

interface PaginatedFilter {
  key: string;
  is_archived: boolean;
  page: PageData;
}

interface IPagination<T> {
  length: number;
  list: T;
}

The Class

class Service {

  constructor(public name) {}

    search(x: BasicFilter): IPagination<Employee[]>;
    search(y: PaginatedFilter): IPagination<Employee[]>;
    search(key: string): IPagination<Employee[]>;
    search(args: any): any {

      if (typeof args === 'string') {
        console.log('searched with keyword only');
      }

      if (typeof args === 'object') {
        console.log('searched with object arguments');
      }

    }

}

Usage

const service = new Service('Serbisyo publiko');

service.search({ key: 'some key', is_archived: false });

const default_page: PageData = { pageIndex: 0, pageSize: 15 };

service.search({ key: 'some queries', is_archived: true, page: default_page });

service.search('filtering data..');

The Output

searched with object arguments
searched with object arguments
searched with keyword only

DEMO

Upvotes: 1

Views: 74

Answers (2)

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 250366

Since interfaces don't exist at runtime you can't type guard them them with typeof (which works for primitive types) or instanceof (which would work for classes). You can use an in type guard which discriminates a union based on the existence of a fields. Also I would use a union of the possible parameter types for the implementation signature so type guards work correctly and you have full type safety. In your case the page field appers to be a good candidate for this:

search(x: BasicFilter): IPagination<Employee[]>;
search(y: PaginatedFilter): IPagination<Employee[]>;
search(key: string): IPagination<Employee[]>;
search(args: BasicFilter | PaginatedFilter | string): any {

    if (typeof args === 'string') {
        console.log('searched with keyword only');
        args // is typed to string here
    }
    else if ('page' in  args) {
        console.log('searched with object arguments');
        args // is typed to PaginatedFilter here
    }else {
        args // is typed to BasicFilter here
    }
}

Note Order of type guards matters, you have to remove the string from the union first as the in type guard will only work if all types in the union are objects.

Upvotes: 2

Amit Saxena
Amit Saxena

Reputation: 1

From your question it can be seen that currently there are three search possibilities exposed through different interface like Basic Filter, Paginated Filter etc and also string. Tommorow it may so happen that you may need to come up with another interface that has diferent search filter.

To provide extensibilty and a comon searching terminolgy that helps you achieve OCP, please have all your interfaces derive from another dummy interface like IFilterCriteria. This means Basic Filter, Paginated Filter will now derive from IFilterCriteria. Also to provide for strign based search creater another interface called StringFilter will also derive from IFilterCriteria.

IFilterCriteria interface will be defined as:

interface IFilterCriteria {
}

This means it do not hold any state. It is just a marker interface

BasicFilter interface now will be defined as

interface BasicFilter extends IFilterCriteria {
    key: string;
    is_archived: boolean;
}

Similarly we can have modified definition of

interface PaginatedFilter extends IFilterCriteria {
    key: string;
    is_archived: boolean;
    page: PageData;
}

Thus this provides common ground all current and future filtering possibilities.

And now the search function becomes more type safe and its signature will be:

search(args: IFilterCriteria): any;

Since now search takes this IFilterCriteria interface as parameter it can safely be type-casted and checked within function body

Upvotes: 0

Related Questions