The errors you are seeing are a combination of a bug in your code and a bug in Flow.
Bug in your code
Let’s start by fixing your bug. In the third else statement, you assign the wrong value to
} else {
name="someone";
age = args[1]; // <-- Should be index 0
}
Changing the array access to be the correct index removes two errors. I think we can both agree this is exactly what Flow is for, finding errors in your code.
Narrowing type
In order to get to the root cause of the issue, we can be more explicit in the area where the errors are so that we can more easily see what the problem is:
if (args.length > 1) {
const args_tuple: [string, number] = args;
name = args_tuple[0];
age = args_tuple[1];
} else if (typeof args[0] === 'string') {
This is effectively the same as before but because we’re very clear about what args[0]
and args[1]
should be at this point. This leaves us with a single error.
Bug in Flow
The remaining error is a bug in Flow: https://github.com/facebook/flow/issues/3564
bug: tuple type is not interacting with length assertions (.length >= 2 and [] | [number] | [number, number] type)
How to type overloaded functions
Flow is not great at dealing with variadics with different types, as in this case. Variadics are more for stuff like function sum(...args: Array<number>)
where all the types are the same and there is no maximum arity.
Instead, you should be more explicit with your arguments, like so:
const foo = (name: string | number, age?: number) => {
let real_name: string = 'someone';
let real_age: number = 0;
// unpack args...
if (typeof name === 'number') {
real_age = name;
} else {
real_name = name;
real_age = age || 0;
}
console.log(`${real_name} is ${real_age}`);
};
// any of these call signatures should be OK:
foo('fred');
foo('fred', 30);
foo(30);
This causes no errors and I think is just easier to read for developers, too.
A better way
In another answer, Pavlo provided another solution that I like more than my own.
type Foo =
& ((string | number) => void)
& ((string, number) => void)
const foo: Foo = (name, age) => {...};
It solves the same problems in a much cleaner way, allowing you much more flexibility. By creating an intersection of multiple function types, you describe each different way of calling your function, allowing Flow to try each one based on how the function is called.