Need advice on how to model overloaded functions (might be impossble)

Topics: General
Oct 9, 2013 at 4:44 AM
Edited Oct 9, 2013 at 4:45 AM
I'm creating typings for a existing module (Q-io/fs) and have some problems with some functions that can return different types of objects depending on an options argument:

This is not acceptable for the compiler (overload cannot differ only in return types):
export function open(path:string, options?:any):Q.Promise<Qio.Reader>;
export function open(path:string, options?:any):Q.Promise<Qio.Writer>;
export function open(path:string, options?:any):Q.Promise<Qio.Stream>;
export function open(path:string, options?:any):Q.Promise<NodeBuffer>;
export function open(path:string, options?:any):Q.Promise<string>;
The options has a few properties that will determine what gets returned but it requires some logic: the existence of property 'charset' and a 'flags' property that is a string made up of some concatenated flag names (combination of r, w, a, b).

I think this is impossible to model directly. How would you solve this?
Oct 10, 2013 at 11:53 AM
Edited Oct 10, 2013 at 12:05 PM
What you want is the following, which is not possible:
function open(path:string, options?:{ flags: "r"}):Q.Promise<Qio.Reader>;
function open(path:string, options?:{ flags: "w"}):Q.Promise<Qio.Writer>;
One solution is to explicitly model the options:
interface Options {
interface ReadOptions extends Options { isRead: boolean;}  // A dummy property 
interface WriteOptions extends Options {}

function open(path:string, options?:ReadOptions): Q.Promise<Qio.Reader>;;
function open(path:string, options?:WriteOptions): Q.Promise<Qio.Writer>;

If there are no differences between the read and write options then you are forced to define a dummy property in order to force a distinction.

A generic solution is also possible, but that would require the caller to specify the return type.
function open<T>(path:string, options?:any): Q.Promise<T>;

var x = open<Qio.Reader>("foo", { flags: "r"});
var y = open<Qio.Writer>("foo", { flags: "w"});
Oct 10, 2013 at 2:41 PM
Nice, I think I can make either of those those work (I can't actually confirm that right now as it would be part of a larger migration that may take a little time to implement).

Thanks for the input!