Somewhere in my Flow annotated JavaScript code I have an object obj defined with nullable field field.
let obj: { field: ?number } = { field: 1 };
I have a function func that accepts argument arg which has the same structure as obj but field should be non-nullable at this time.
function func(arg: {field: number}){
// do something
}
I'm calling the func with obj after checking that the obj.field is not null or undefined, hoping that Flow will do it's type refining magic and understand that obj.field is always number and it's type is OK to pass to the func.
if(obj.field !== null && obj.field !== undefined){
func(obj);
}
Surprisingly getting an error, Flow doesn't like it:
14: func(obj);
^ Cannot call `func` with `obj` bound to `arg` because null or undefined [1] is incompatible with number [2] in property `field`.
References:
4: field: ?number ^ [1]
9: function func(arg: {field: number}){
^ [2]
Run online in the Try Flow
Two questions:
The code snippets above are simplified version of the code to demonstrate the problem. Actual obj is more complex.
First-off, Flow's refinement applies to individual fields here, but generally you aren't refining the whole object's type, so you can't refine an object's type based on refinement of a single field (as far as I'm aware). Even if you could, this example would still not be typesafe. Here's something that illustrates the problem with allowing this:
let obj: { field: ?number } = { field: 1 };
if(obj.field !== null && obj.field !== undefined){
func(obj);
obj.field = null;
}
function func(arg: {field: number}){
setTimeout(() => {
var value: number = arg.field;
}, 10);
}
Given purely the restrictions you're looking for, this would typecheck, but the value inside the func timeout callback would actually be null.
The only way your given code could be safe with typechecking is if the arg.field value were marked contravariant (note the - in the property name for func now, e.g.
let obj: { field: ?number } = { field: 1 };
if(obj.field !== null && obj.field !== undefined){
func(obj);
}
function func(arg: {-field: number}){
arg.field = 43;
}
(On Flow Try)
This effectively would make arg.field write-only, which is fine because writing a number to field would always be safe, but reading one is not safe in the long term.
What is best practice to make it work?
It depends on what your real code does, to some extent. In this specific example I'd say pass the number directly as the argument, but assuming it needs to be an object, you could consider creating a new object with just the properties func needs.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With