Example 1:
type A = $ObjMapi<{ FOO: null }, <K>(k:K) => K>
declare var a: A;
(a.FOO : 'FOO'); // ok
(a.FOO : 'BAR'); // error
a.FOO = 'BAR'; // no error
Example 2:
type A = $ObjMap<{ FOO: null }, <K>(k:K) => 'FOO'>
declare var a: A;
(a.FOO : 'FOO'); // ok
(a.FOO : 'BAR'); // error
a.FOO = 'BAR'; // no error
Example 3:
type A = $TupleMap<[mixed, mixed], <K>(k:K) => 'FOO'>
declare var a: A;
(a[0] : 'FOO'); // ok
(a[0]: 'BAR'); // error
a[0] = 'BAR'; // no error
See: https://github.com/gcanti/flow-runtime/issues/11
/cc @gcanti
This affects only setting properties and literal assignment:
var a: A = { FOO: 'BAR' }; // no error, but should be
but:
declare var b: { FOO: 'BAR' };
var a: A = b; // an error, as expected
and:
type A = $ObjMap<{ FOO: null }, <K>(k:K) => 'FOO'>
declare var b: { FOO: number };
var a: A = b; // an error, as expected
but:
declare var b: { FOO: string };
var a: A = b; // no error, but should be
You're right, this is a problem. The cause in the implementation is that we use the existing mechanism for function calls to perform the mapping. This is convenient, but inherits an unfortunate behavior:
declare function f(): string;
var x = f();
if (true) x = 0; // cond. assign to avoid definite assignment refinement
(x: void); // errors: number|string ~> void
Calling a function returns a new, open tvar with the function return type as a lower bound. This doesn't prevent more lower bounds from flowing in, but all inflowing lower bounds are checked against any uses, so it's not totally unsound.
Just explaining this behavior, not excusing it. We should work to make the generate type more precise, because unlike function calls, these types come from annotations where people rightfully expect to establish an exact constraint, not just the lower bound side of one.
@samwgoldman I though it was the case, but how does it explain this:
type A = $ObjMapi<{ FOO: null }, <K>(k:K) => K>
declare var a: A;
(a.FOO : 'FOO'); // ok
a.FOO = 'BAR'; // no error, a.FOO should be 'FOO' | 'BAR'
(a.FOO : 'FOO'); // error
That's caused by definite assignment analysis. On that line the value is definitely BAR. Wrap in a conditional or otherwise havoc the refinements to get the union.
Hey, just wanted to see if there were any plans for this. Would be pretty cool to have this working properly!
These bugs were fixed a couple versions back. Sorry for not closing this sooner 馃槉
This is a great news, thanks @calebmer
Most helpful comment
Hey, just wanted to see if there were any plans for this. Would be pretty cool to have this working properly!