This code snippet causes an error:
type Func = (obj?: Object) => Object;
const g: Func = (obj: ?Object): Object => obj || {};
const f = (obj: ?Object): Object => {
return g(obj); // this line causes an error
};
whereas this code snippet (I've added || undefined) works:
type Func = (obj?: Object) => Object;
const g: Func = (obj: ?Object): Object => obj || {};
const f = (obj: ?Object): Object => {
return g(obj || undefined);
};
Declaring in the type Func first parameter as obj?: Object is different from obj: ?Object, even if in javascript it has a similar behaviour.
To do what I think you mean with your example, you should declare Func as:
type Func = (obj: ?Object) => Object;
You can see it here working without errors.
obj?: Object: The parameter is optional, so you can call a Func with or without the parameter set, however if obj _is passed_ it should be a not-null Object.obj: ?Object: The parameter _should_ be passed, however flow will not complain if you call only g(); cause it's the same as g(undefined); which is allowed by the function declaration.g(null); is even allowed, so your function will not complain about a potentially null obj.Hi @leonardfactory, thanks so much for taking the time to explain where I've been going wrong — I'd come to the spurious conclusion that one form was used for functions while the other type was used for type definitions! The bonus of knowing that there's a semantic difference between these forms will no doubt come in handy down the line :smile:.
In case it helps anyone else, it's worth pointing out that although you can use both forms (obj?: Object and obj: ?Object) in _type-definitions_, you can only use the obj: ?Object form in _functions_ themselves.
When you need obj? Object within a function, you instead use the ES6 default parameter syntax (e.g. obj: Object = {}) to denote that the parameter is optional, and this then also saves you adding extra code within your function to deal with the fact that the parameter may not have been defined.
@dchambers I'm not sure that's accurate, unless I misunderstand. This function is perfectly fine:
function f(x?: string): string {
if (typeof x === "undefined") {
return "default string";
} else {
return x;
}
}
Now, you're correct that providing a default value has the effect of making the argument optional in the function type and non-optional in the method body, which is the natural behavior in my opinion. But it's totally fine to make an argument optional and deal with the possible undefined within the function.
Hi @samwgoldman, maybe you're using a more recent version of Flow than I am, but it doesn't work for me, and it doesn't work if I modify the code in the demo link provided earlier either.
BTW, the fact I couldn't do that is actually how I came to the erroneous conclusion that one form was for _functions_ and the other for _type-definitions_ in the first place.
Looks like a Babel bug, actually, and only affects optional arguments in arrow functions. Flow parses and types it successfully, but compilation fails. Will check with Babel folks to see if they're aware of this issue.
Thanks @samwgoldman :+1:.
Issue is being tracked here: https://phabricator.babeljs.io/T7096
Thanks again for taking the time to look into this :smile:.
Most helpful comment
Declaring in the type
Funcfirst parameter asobj?: Objectis different fromobj: ?Object, even if in javascript it has a similar behaviour.TLDR
To do what I think you mean with your example, you should declare
Funcas:You can see it here working without errors.
Further explanation
obj?: Object: The parameter is optional, so you can call aFuncwith or without the parameter set, however if obj _is passed_ it should be a not-null Object.obj: ?Object: The parameter _should_ be passed, however flow will not complain if you call onlyg();cause it's the same asg(undefined);which is allowed by the function declaration.However in this case,
g(null);is even allowed, so your function will not complain about a potentiallynullobj.