Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Function overload best practices #4305

Closed
StreetStrider opened this issue Jul 3, 2017 · 6 comments
Closed

Function overload best practices #4305

StreetStrider opened this issue Jul 3, 2017 · 6 comments

Comments

@StreetStrider
Copy link

I'm often stagger against need to describe in Flow functions which can be invoked with several different arguments' typesets. This is a common practice for JS to allow functions to work flexible depending on argument types.

Consider the following example:
I need to describe a function which accepts optional argument in first position. Something like a flag:

foo(true, 1, 2, 3)
foo(1, 2, 3)

When you put your optional flag argument on the first place it makes flag more clear to read and less miss-prone. The package extend uses this manner for its deep flag.

So, my synthetic example for such case:

// F is a such function type described via union
type F
= ((x: string, ...args: number[]) => number)
| ((...args: number[]) => number)
// retval is homohenous

Then the function which implement this can be the following:

var first_number: F = function (...args)
{
	if (typeof args[0] === 'boolean')
	{
		return args[1]
	}
	else
	{
		return args[0]
	}
}

The flag is ignored, but it don't make the idea less clear.

I'm using union (and not intersection) and expecting this definition to work OK. However I got the error:

  4: = ((x: boolean, ...args: number[]) => number)
            ^^^^^^^ boolean. This type is incompatible with
  4: = ((x: boolean, ...args: number[]) => number)
                                           ^^^^^^ number

just if I was using intersection.

This is not the first time I'm struggling with describing polymorphic/variadic functions in Flow. This whole part of the typesystem is obscure for me. Will glad for any tips and help.

@rhendric
Copy link
Contributor

I think you want an intersection type here: a value of type F can be used both as a function of the first type and as a function of the second type; it isn't one or the other. (Easy mistake to make when you're thinking about the function's definition, since you have to flip your ands and ors inside the function body: if the function is a string => X and a number => X, inside the function you have to handle a value that is a string or a number. The super-nerdy way to say this is that function parameter types are contravariant.) Not that it helps you practically (yet), because Flow doesn't check the definitions of function intersection types either. But the thing to ask for is intersections.

@StreetStrider
Copy link
Author

@rhendric thanks for the tip about ANDs and ORs and their flipping. I've already faced some topic related this question after I post this issue. I've spent some time overcoming this misunderstanding in my mind.

Looks like for now Flow is working OK (and checks proper types) when intersection is in use.

Flow also allows direct function overload (in similar to TS manner) inside declaration files, by putting multiple function declarations of the same function name.

@oriSomething
Copy link

You can use function overloading inside non-declaration files like this:

declare function first_number(x: string, ...args: number[]) => number;
declare function first_number(...args: number[]) => number;
function first_number (...args) {
  if (typeof args[0] === 'boolean')
  {
    return args[1]
  }
  else
  {
    return args[0]
  }
}

@rhendric
Copy link
Contributor

@oriSomething Why does this report no errors?

@oriSomething
Copy link

@rhendric

There is no "official" support for that. The non "declared" function treat is as any but if you try to call the function it will show you error comparing to the declaration

var x: string = first_number(1); // Will emit error

@zeorin
Copy link

zeorin commented Jul 11, 2018

Interfaces seem to have the behaviour you're looking for: #3021 (comment).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants