pocesar
pocesar

Reputation: 7050

Overloads cannot differ only by return type

I'm trying to overload a function so when using the function somewhere else, it will show the result properly that is either an array of items, void, or a single item:

getSelected(type): void;
getSelected(type): IDataItem;
getSelected(type): IDataItem[];
getSelected(type:string, one:boolean = true):any {
   if (!type) {
       return;
   }
   if (one) {
      return _.reduce<IDataItem, IDataItem>(sections[type], (current: IDataItem, p: IDataItem) => {
         return p.selected === true ? p : current;
      }, void 0);
   }

   return _.filter<IDataItem>(sections[type], (p: IDataItem):boolean => {
     return p.selected === true && p.enabled === true;
   });
}

It's giving me the error "error TS2175: Overloads cannot differ only by return type.". How can I just signal the many possibilities for the return types then?

Upvotes: 4

Views: 5850

Answers (3)

Fenton
Fenton

Reputation: 250952

It is worth noting a couple of points about your original code...

You cannot call the implementation signature.

These are "overload signatures" - you can call these.

getSelected(type): void;
getSelected(type): IDataItem;
getSelected(type): IDataItem[];

The next line is the "implementation signature" - you cannot call it directly.

getSelected(type:string, one:boolean = true):any {

This means that as it stands, you cannot ever pass the one argument.

You need to type the type parameter.

Your overloads don't specify a type for the type parameter, these will have the any type - but I think you probably want to restrict this to strings. Each signature needs the annotation...

type: string

Differ based on boolean argument value

Your signature that states that if you pass true in the one argument, you'll get a single result. If you were to pass false, you'd get multiple results. We can now work with that, because TypeScript has been made even more awesome. See below...

Working overloads.

Given all this information, you can use:

getSelected(type: string): void;
getSelected(type: string, one: true): IDataItem;
getSelected(type: string, one: false): IDataItem[];
getSelected(type: string, one: boolean = true): any {
    // ... code!
}

When you call this, the types will be inferred based on the arguments passed. This is the shape of it, with your code removed from the actual method...

interface IDataItem {
    name: string;
}

class Example {
    getSelected(type: string): void;
    getSelected(type: string, one: true): IDataItem;
    getSelected(type: string, one: false): IDataItem[];
    getSelected(type: string, one: boolean = true): void | IDataItem | IDataItem[] {
        // ... code!
    }
}

const example = new Example();

// void return
example.getSelected('just type');

// IDataItem
const a = example.getSelected('x', true);

// IDataItem[]
const b = example.getSelected('x', false);

Upvotes: 15

Ryan Cavanaugh
Ryan Cavanaugh

Reputation: 221034

If you might return anything based on the content of data passed in, and there's no common type between those possible return values, you should declare your return type to be any and force the calling code to use a type assertion.

I don't like Paleo's suggestion because it implies type safety where there is none.

Returning different types of data from a single function call is a code smell; it puts a lot of onus on the caller to understand what the relationship is between the inputs and the outputs. You'd be better off having something like getSelectedSingle, getSelectedMultiple, and getSelectedNone.

Upvotes: 3

Paleo
Paleo

Reputation: 23702

Use generics in order to help the compiler to determine the right value:

getSelected<T>(type:string, one:boolean = true): T {
    // ...
}

getSelected<void>('abc');
var aDataItem = getSelected<IDataItem>('abc');
var aList = getSelected<IDataItem[]>('abc');

See also the documentation.

Upvotes: 3

Related Questions