I checked that foo.bar
is not null
, but Flow still thinks it is. Why does this happen and how can I fix it?
Flow does not keep track of side effects, so any function call may potentially nullify your check.
This is called refinement invalidation.
Example (https://flow.org/try):
1
2
3
4
5
6
7
8
9
10
11
12
|
type Param = {
bar: ?string,
}
function myFunc(foo: Param): string {
if (foo.bar) {
console.log("checked!");
return foo.bar;
}
return "default string";
}
|
Cannot return `foo.bar` because null or undefined [1] is incompatible with string [2]. [incompatible-return]
{"value":"// @flow\ntype Param = {\n bar: ?string,\n}\nfunction myFunc(foo: Param): string {\n if (foo.bar) {\n console.log(\"checked!\");\n return foo.bar; // Flow errors. If you remove the console.log, it works\n }\n\n return \"default string\";\n}\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_TYPE","context":"normal","value":"type","line":2,"start":9,"end":13},{"type":"T_IDENTIFIER","context":"type","value":"Param","line":2,"start":14,"end":19},{"type":"T_ASSIGN","context":"type","value":"=","line":2,"start":20,"end":21},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":22,"end":23},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":3,"start":26,"end":29},{"type":"T_COLON","context":"type","value":":","line":3,"start":29,"end":30},{"type":"T_PLING","context":"type","value":"?","line":3,"start":31,"end":32},{"type":"T_STRING_TYPE","context":"type","value":"string","line":3,"start":32,"end":38},{"type":"T_COMMA","context":"type","value":",","line":3,"start":38,"end":39},{"type":"T_RCURLY","context":"type","value":"}","line":4,"start":40,"end":41},{"type":"T_FUNCTION","context":"normal","value":"function","line":5,"start":42,"end":50},{"type":"T_IDENTIFIER","context":"normal","value":"myFunc","line":5,"start":51,"end":57},{"type":"T_LPAREN","context":"normal","value":"(","line":5,"start":57,"end":58},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":5,"start":58,"end":61},{"type":"T_COLON","context":"type","value":":","line":5,"start":61,"end":62},{"type":"T_IDENTIFIER","context":"type","value":"Param","line":5,"start":63,"end":68},{"type":"T_RPAREN","context":"normal","value":")","line":5,"start":68,"end":69},{"type":"T_COLON","context":"type","value":":","line":5,"start":69,"end":70},{"type":"T_STRING_TYPE","context":"type","value":"string","line":5,"start":71,"end":77},{"type":"T_LCURLY","context":"normal","value":"{","line":5,"start":78,"end":79},{"type":"T_IF","context":"normal","value":"if","line":6,"start":82,"end":84},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":85,"end":86},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":6,"start":86,"end":89},{"type":"T_PERIOD","context":"normal","value":".","line":6,"start":89,"end":90},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":6,"start":90,"end":93},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":93,"end":94},{"type":"T_LCURLY","context":"normal","value":"{","line":6,"start":95,"end":96},{"type":"T_IDENTIFIER","context":"normal","value":"console","line":7,"start":101,"end":108},{"type":"T_PERIOD","context":"normal","value":".","line":7,"start":108,"end":109},{"type":"T_IDENTIFIER","context":"normal","value":"log","line":7,"start":109,"end":112},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":112,"end":113},{"type":"T_STRING","context":"normal","value":"\"checked!\"","line":7,"start":113,"end":123},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":123,"end":124},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":124,"end":125},{"type":"T_RETURN","context":"normal","value":"return","line":8,"start":130,"end":136},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":8,"start":137,"end":140},{"type":"T_PERIOD","context":"normal","value":".","line":8,"start":140,"end":141},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":8,"start":141,"end":144},{"type":"T_SEMICOLON","context":"normal","value":";","line":8,"start":144,"end":145},{"type":"Line","context":"comment","value":"// Flow errors. If you remove the console.log, it works","line":8,"start":146,"end":201},{"type":"T_RCURLY","context":"normal","value":"}","line":9,"start":204,"end":205},{"type":"T_RETURN","context":"normal","value":"return","line":11,"start":209,"end":215},{"type":"T_STRING","context":"normal","value":"\"default string\"","line":11,"start":216,"end":232},{"type":"T_SEMICOLON","context":"normal","value":";","line":11,"start":232,"end":233},{"type":"T_RCURLY","context":"normal","value":"}","line":12,"start":234,"end":235}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot return `foo.bar` because null or undefined [1] is incompatible with string [2]. [incompatible-return]","context":" return foo.bar; // Flow errors. If you remove the console.log, it works","source":"-","start":{"line":8,"column":12,"offset":137},"end":{"line":8,"column":18,"offset":144}}],"operation":null}]}
You can get around this by storing your checked values in local variables:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
type Param = {
bar: ?string,
}
function myFunc(foo: Param): string {
if (foo.bar) {
const bar = foo.bar;
console.log("checked!");
return bar;
}
return "default string";
}
|
{"value":"// @flow\ntype Param = {\n bar: ?string,\n}\nfunction myFunc(foo: Param): string {\n if (foo.bar) {\n const bar = foo.bar;\n console.log(\"checked!\");\n return bar; // Ok!\n }\n\n return \"default string\";\n}\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_TYPE","context":"normal","value":"type","line":2,"start":9,"end":13},{"type":"T_IDENTIFIER","context":"type","value":"Param","line":2,"start":14,"end":19},{"type":"T_ASSIGN","context":"type","value":"=","line":2,"start":20,"end":21},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":22,"end":23},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":3,"start":26,"end":29},{"type":"T_COLON","context":"type","value":":","line":3,"start":29,"end":30},{"type":"T_PLING","context":"type","value":"?","line":3,"start":31,"end":32},{"type":"T_STRING_TYPE","context":"type","value":"string","line":3,"start":32,"end":38},{"type":"T_COMMA","context":"type","value":",","line":3,"start":38,"end":39},{"type":"T_RCURLY","context":"type","value":"}","line":4,"start":40,"end":41},{"type":"T_FUNCTION","context":"normal","value":"function","line":5,"start":42,"end":50},{"type":"T_IDENTIFIER","context":"normal","value":"myFunc","line":5,"start":51,"end":57},{"type":"T_LPAREN","context":"normal","value":"(","line":5,"start":57,"end":58},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":5,"start":58,"end":61},{"type":"T_COLON","context":"type","value":":","line":5,"start":61,"end":62},{"type":"T_IDENTIFIER","context":"type","value":"Param","line":5,"start":63,"end":68},{"type":"T_RPAREN","context":"normal","value":")","line":5,"start":68,"end":69},{"type":"T_COLON","context":"type","value":":","line":5,"start":69,"end":70},{"type":"T_STRING_TYPE","context":"type","value":"string","line":5,"start":71,"end":77},{"type":"T_LCURLY","context":"normal","value":"{","line":5,"start":78,"end":79},{"type":"T_IF","context":"normal","value":"if","line":6,"start":82,"end":84},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":85,"end":86},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":6,"start":86,"end":89},{"type":"T_PERIOD","context":"normal","value":".","line":6,"start":89,"end":90},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":6,"start":90,"end":93},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":93,"end":94},{"type":"T_LCURLY","context":"normal","value":"{","line":6,"start":95,"end":96},{"type":"T_CONST","context":"normal","value":"const","line":7,"start":101,"end":106},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":7,"start":107,"end":110},{"type":"T_ASSIGN","context":"normal","value":"=","line":7,"start":111,"end":112},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":7,"start":113,"end":116},{"type":"T_PERIOD","context":"normal","value":".","line":7,"start":116,"end":117},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":7,"start":117,"end":120},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":120,"end":121},{"type":"T_IDENTIFIER","context":"normal","value":"console","line":8,"start":126,"end":133},{"type":"T_PERIOD","context":"normal","value":".","line":8,"start":133,"end":134},{"type":"T_IDENTIFIER","context":"normal","value":"log","line":8,"start":134,"end":137},{"type":"T_LPAREN","context":"normal","value":"(","line":8,"start":137,"end":138},{"type":"T_STRING","context":"normal","value":"\"checked!\"","line":8,"start":138,"end":148},{"type":"T_RPAREN","context":"normal","value":")","line":8,"start":148,"end":149},{"type":"T_SEMICOLON","context":"normal","value":";","line":8,"start":149,"end":150},{"type":"T_RETURN","context":"normal","value":"return","line":9,"start":155,"end":161},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":9,"start":162,"end":165},{"type":"T_SEMICOLON","context":"normal","value":";","line":9,"start":165,"end":166},{"type":"Line","context":"comment","value":"// Ok!","line":9,"start":167,"end":173},{"type":"T_RCURLY","context":"normal","value":"}","line":10,"start":176,"end":177},{"type":"T_RETURN","context":"normal","value":"return","line":12,"start":181,"end":187},{"type":"T_STRING","context":"normal","value":"\"default string\"","line":12,"start":188,"end":204},{"type":"T_SEMICOLON","context":"normal","value":";","line":12,"start":204,"end":205},{"type":"T_RCURLY","context":"normal","value":"}","line":13,"start":206,"end":207}],"errors":[]}
I checked that my object is of type A, so why does Flow still believe it’s A | B?
Refinement invalidation can also happen with disjoint unions. Any function call will invalidate any refinement.
Example (https://flow.org/try):
1
2
3
4
5
6
7
8
9
10
11
12
|
type Response =
| { type: 'success', value: string }
| { type: 'error', error: Error };
const handleResponse = (response: Response) => {
if (response.type === 'success') {
setTimeout(() => {
console.log(`${response.value} 1`)
}, 1000);
}
};
|
{"value":"// @flow\ntype Response =\n | { type: 'success', value: string }\n | { type: 'error', error: Error };\n\nconst handleResponse = (response: Response) => {\n if (response.type === 'success') {\n setTimeout(() => {\n console.log(`${response.value} 1`)\n }, 1000);\n }\n};\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_TYPE","context":"normal","value":"type","line":2,"start":9,"end":13},{"type":"T_IDENTIFIER","context":"type","value":"Response","line":2,"start":14,"end":22},{"type":"T_ASSIGN","context":"type","value":"=","line":2,"start":23,"end":24},{"type":"T_BIT_OR","context":"type","value":"|","line":3,"start":27,"end":28},{"type":"T_LCURLY","context":"type","value":"{","line":3,"start":29,"end":30},{"type":"T_TYPE","context":"normal","value":"type","line":3,"start":31,"end":35},{"type":"T_COLON","context":"type","value":":","line":3,"start":35,"end":36},{"type":"T_STRING","context":"type","value":"'success'","line":3,"start":37,"end":46},{"type":"T_COMMA","context":"type","value":",","line":3,"start":46,"end":47},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":3,"start":48,"end":53},{"type":"T_COLON","context":"type","value":":","line":3,"start":53,"end":54},{"type":"T_STRING_TYPE","context":"type","value":"string","line":3,"start":55,"end":61},{"type":"T_RCURLY","context":"type","value":"}","line":3,"start":62,"end":63},{"type":"T_BIT_OR","context":"type","value":"|","line":4,"start":66,"end":67},{"type":"T_LCURLY","context":"type","value":"{","line":4,"start":68,"end":69},{"type":"T_TYPE","context":"normal","value":"type","line":4,"start":70,"end":74},{"type":"T_COLON","context":"type","value":":","line":4,"start":74,"end":75},{"type":"T_STRING","context":"type","value":"'error'","line":4,"start":76,"end":83},{"type":"T_COMMA","context":"type","value":",","line":4,"start":83,"end":84},{"type":"T_IDENTIFIER","context":"normal","value":"error","line":4,"start":85,"end":90},{"type":"T_COLON","context":"type","value":":","line":4,"start":90,"end":91},{"type":"T_IDENTIFIER","context":"type","value":"Error","line":4,"start":92,"end":97},{"type":"T_RCURLY","context":"type","value":"}","line":4,"start":98,"end":99},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":99,"end":100},{"type":"T_CONST","context":"normal","value":"const","line":6,"start":102,"end":107},{"type":"T_IDENTIFIER","context":"normal","value":"handleResponse","line":6,"start":108,"end":122},{"type":"T_ASSIGN","context":"normal","value":"=","line":6,"start":123,"end":124},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":125,"end":126},{"type":"T_IDENTIFIER","context":"normal","value":"response","line":6,"start":126,"end":134},{"type":"T_COLON","context":"type","value":":","line":6,"start":134,"end":135},{"type":"T_IDENTIFIER","context":"type","value":"Response","line":6,"start":136,"end":144},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":144,"end":145},{"type":"T_ARROW","context":"normal","value":"=>","line":6,"start":146,"end":148},{"type":"T_LCURLY","context":"normal","value":"{","line":6,"start":149,"end":150},{"type":"T_IF","context":"normal","value":"if","line":7,"start":153,"end":155},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":156,"end":157},{"type":"T_IDENTIFIER","context":"normal","value":"response","line":7,"start":157,"end":165},{"type":"T_PERIOD","context":"normal","value":".","line":7,"start":165,"end":166},{"type":"T_TYPE","context":"normal","value":"type","line":7,"start":166,"end":170},{"type":"T_STRICT_EQUAL","context":"normal","value":"===","line":7,"start":171,"end":174},{"type":"T_STRING","context":"normal","value":"'success'","line":7,"start":175,"end":184},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":184,"end":185},{"type":"T_LCURLY","context":"normal","value":"{","line":7,"start":186,"end":187},{"type":"T_IDENTIFIER","context":"normal","value":"setTimeout","line":8,"start":192,"end":202},{"type":"T_LPAREN","context":"normal","value":"(","line":8,"start":202,"end":203},{"type":"T_LPAREN","context":"normal","value":"(","line":8,"start":203,"end":204},{"type":"T_RPAREN","context":"normal","value":")","line":8,"start":204,"end":205},{"type":"T_ARROW","context":"normal","value":"=>","line":8,"start":206,"end":208},{"type":"T_LCURLY","context":"normal","value":"{","line":8,"start":209,"end":210},{"type":"T_IDENTIFIER","context":"normal","value":"console","line":9,"start":217,"end":224},{"type":"T_PERIOD","context":"normal","value":".","line":9,"start":224,"end":225},{"type":"T_IDENTIFIER","context":"normal","value":"log","line":9,"start":225,"end":228},{"type":"T_LPAREN","context":"normal","value":"(","line":9,"start":228,"end":229},{"type":"T_TEMPLATE_PART","context":"normal","value":"`${","line":9,"start":229,"end":232},{"type":"T_IDENTIFIER","context":"normal","value":"response","line":9,"start":232,"end":240},{"type":"T_PERIOD","context":"normal","value":".","line":9,"start":240,"end":241},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":9,"start":241,"end":246},{"type":"T_TEMPLATE_PART","context":"template","value":"} 1`","line":9,"start":246,"end":250},{"type":"T_RPAREN","context":"normal","value":")","line":9,"start":250,"end":251},{"type":"T_RCURLY","context":"normal","value":"}","line":10,"start":256,"end":257},{"type":"T_COMMA","context":"normal","value":",","line":10,"start":257,"end":258},{"type":"T_NUMBER","context":"normal","value":"1000","line":10,"start":259,"end":263},{"type":"T_RPAREN","context":"normal","value":")","line":10,"start":263,"end":264},{"type":"T_SEMICOLON","context":"normal","value":";","line":10,"start":264,"end":265},{"type":"T_RCURLY","context":"normal","value":"}","line":11,"start":268,"end":269},{"type":"T_RCURLY","context":"normal","value":"}","line":12,"start":270,"end":271},{"type":"T_SEMICOLON","context":"normal","value":";","line":12,"start":271,"end":272}],"errors":[]}
Here, a work around would be to extract the part of the value you’re interested in, or to move the if check inside the setTimeout
call:
Example (https://flow.org/try):
1
2
3
4
5
6
7
8
9
10
11
12
13
|
type Response =
| { type: 'success', value: string }
| { type: 'error', error: Error };
const handleResponse = (response: Response) => {
if (response.type === 'success') {
const value = response.value
setTimeout(() => {
console.log(`${value} 1`)
}, 1000);
}
};
|
{"value":"// @flow\ntype Response =\n | { type: 'success', value: string }\n | { type: 'error', error: Error };\n\nconst handleResponse = (response: Response) => {\n if (response.type === 'success') {\n const value = response.value\n setTimeout(() => {\n console.log(`${value} 1`)\n }, 1000);\n }\n};\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_TYPE","context":"normal","value":"type","line":2,"start":9,"end":13},{"type":"T_IDENTIFIER","context":"type","value":"Response","line":2,"start":14,"end":22},{"type":"T_ASSIGN","context":"type","value":"=","line":2,"start":23,"end":24},{"type":"T_BIT_OR","context":"type","value":"|","line":3,"start":27,"end":28},{"type":"T_LCURLY","context":"type","value":"{","line":3,"start":29,"end":30},{"type":"T_TYPE","context":"normal","value":"type","line":3,"start":31,"end":35},{"type":"T_COLON","context":"type","value":":","line":3,"start":35,"end":36},{"type":"T_STRING","context":"type","value":"'success'","line":3,"start":37,"end":46},{"type":"T_COMMA","context":"type","value":",","line":3,"start":46,"end":47},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":3,"start":48,"end":53},{"type":"T_COLON","context":"type","value":":","line":3,"start":53,"end":54},{"type":"T_STRING_TYPE","context":"type","value":"string","line":3,"start":55,"end":61},{"type":"T_RCURLY","context":"type","value":"}","line":3,"start":62,"end":63},{"type":"T_BIT_OR","context":"type","value":"|","line":4,"start":66,"end":67},{"type":"T_LCURLY","context":"type","value":"{","line":4,"start":68,"end":69},{"type":"T_TYPE","context":"normal","value":"type","line":4,"start":70,"end":74},{"type":"T_COLON","context":"type","value":":","line":4,"start":74,"end":75},{"type":"T_STRING","context":"type","value":"'error'","line":4,"start":76,"end":83},{"type":"T_COMMA","context":"type","value":",","line":4,"start":83,"end":84},{"type":"T_IDENTIFIER","context":"normal","value":"error","line":4,"start":85,"end":90},{"type":"T_COLON","context":"type","value":":","line":4,"start":90,"end":91},{"type":"T_IDENTIFIER","context":"type","value":"Error","line":4,"start":92,"end":97},{"type":"T_RCURLY","context":"type","value":"}","line":4,"start":98,"end":99},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":99,"end":100},{"type":"T_CONST","context":"normal","value":"const","line":6,"start":102,"end":107},{"type":"T_IDENTIFIER","context":"normal","value":"handleResponse","line":6,"start":108,"end":122},{"type":"T_ASSIGN","context":"normal","value":"=","line":6,"start":123,"end":124},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":125,"end":126},{"type":"T_IDENTIFIER","context":"normal","value":"response","line":6,"start":126,"end":134},{"type":"T_COLON","context":"type","value":":","line":6,"start":134,"end":135},{"type":"T_IDENTIFIER","context":"type","value":"Response","line":6,"start":136,"end":144},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":144,"end":145},{"type":"T_ARROW","context":"normal","value":"=>","line":6,"start":146,"end":148},{"type":"T_LCURLY","context":"normal","value":"{","line":6,"start":149,"end":150},{"type":"T_IF","context":"normal","value":"if","line":7,"start":153,"end":155},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":156,"end":157},{"type":"T_IDENTIFIER","context":"normal","value":"response","line":7,"start":157,"end":165},{"type":"T_PERIOD","context":"normal","value":".","line":7,"start":165,"end":166},{"type":"T_TYPE","context":"normal","value":"type","line":7,"start":166,"end":170},{"type":"T_STRICT_EQUAL","context":"normal","value":"===","line":7,"start":171,"end":174},{"type":"T_STRING","context":"normal","value":"'success'","line":7,"start":175,"end":184},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":184,"end":185},{"type":"T_LCURLY","context":"normal","value":"{","line":7,"start":186,"end":187},{"type":"T_CONST","context":"normal","value":"const","line":8,"start":192,"end":197},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":8,"start":198,"end":203},{"type":"T_ASSIGN","context":"normal","value":"=","line":8,"start":204,"end":205},{"type":"T_IDENTIFIER","context":"normal","value":"response","line":8,"start":206,"end":214},{"type":"T_PERIOD","context":"normal","value":".","line":8,"start":214,"end":215},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":8,"start":215,"end":220},{"type":"T_IDENTIFIER","context":"normal","value":"setTimeout","line":9,"start":225,"end":235},{"type":"T_LPAREN","context":"normal","value":"(","line":9,"start":235,"end":236},{"type":"T_LPAREN","context":"normal","value":"(","line":9,"start":236,"end":237},{"type":"T_RPAREN","context":"normal","value":")","line":9,"start":237,"end":238},{"type":"T_ARROW","context":"normal","value":"=>","line":9,"start":239,"end":241},{"type":"T_LCURLY","context":"normal","value":"{","line":9,"start":242,"end":243},{"type":"T_IDENTIFIER","context":"normal","value":"console","line":10,"start":250,"end":257},{"type":"T_PERIOD","context":"normal","value":".","line":10,"start":257,"end":258},{"type":"T_IDENTIFIER","context":"normal","value":"log","line":10,"start":258,"end":261},{"type":"T_LPAREN","context":"normal","value":"(","line":10,"start":261,"end":262},{"type":"T_TEMPLATE_PART","context":"normal","value":"`${","line":10,"start":262,"end":265},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":10,"start":265,"end":270},{"type":"T_TEMPLATE_PART","context":"template","value":"} 1`","line":10,"start":270,"end":274},{"type":"T_RPAREN","context":"normal","value":")","line":10,"start":274,"end":275},{"type":"T_RCURLY","context":"normal","value":"}","line":11,"start":280,"end":281},{"type":"T_COMMA","context":"normal","value":",","line":11,"start":281,"end":282},{"type":"T_NUMBER","context":"normal","value":"1000","line":11,"start":283,"end":287},{"type":"T_RPAREN","context":"normal","value":")","line":11,"start":287,"end":288},{"type":"T_SEMICOLON","context":"normal","value":";","line":11,"start":288,"end":289},{"type":"T_RCURLY","context":"normal","value":"}","line":12,"start":292,"end":293},{"type":"T_RCURLY","context":"normal","value":"}","line":13,"start":294,"end":295},{"type":"T_SEMICOLON","context":"normal","value":";","line":13,"start":295,"end":296}],"errors":[]}
I’m in a closure and Flow ignores the if check that asserts that foo.bar
is defined. Why?
In the previous section we showed how refinement is lost after a function call. The exact same thing happens within closures, since
Flow does not track how your value might change before the closure is called.
Example (https://flow.org/try):
1
2
3
4
5
6
7
8
9
|
type Person = {age: ?number}
const people = [{age: 12}, {age: 18}, {age: 24}];
const oldPerson: Person = {age: 70};
if (oldPerson.age) {
people.forEach(person => {
console.log(`The person is ${person.age} and the old one is ${oldPerson.age}`);
})
}
|
Cannot coerce `oldPerson.age` to string because null or undefined [1] should not be coerced. [incompatible-type]
{"value":"// @flow\ntype Person = {age: ?number}\nconst people = [{age: 12}, {age: 18}, {age: 24}];\nconst oldPerson: Person = {age: 70};\nif (oldPerson.age) {\n people.forEach(person => {\n console.log(`The person is ${person.age} and the old one is ${oldPerson.age}`);\n })\n}\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_TYPE","context":"normal","value":"type","line":2,"start":9,"end":13},{"type":"T_IDENTIFIER","context":"type","value":"Person","line":2,"start":14,"end":20},{"type":"T_ASSIGN","context":"type","value":"=","line":2,"start":21,"end":22},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":23,"end":24},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":2,"start":24,"end":27},{"type":"T_COLON","context":"type","value":":","line":2,"start":27,"end":28},{"type":"T_PLING","context":"type","value":"?","line":2,"start":29,"end":30},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":2,"start":30,"end":36},{"type":"T_RCURLY","context":"type","value":"}","line":2,"start":36,"end":37},{"type":"T_CONST","context":"normal","value":"const","line":3,"start":38,"end":43},{"type":"T_IDENTIFIER","context":"normal","value":"people","line":3,"start":44,"end":50},{"type":"T_ASSIGN","context":"normal","value":"=","line":3,"start":51,"end":52},{"type":"T_LBRACKET","context":"normal","value":"[","line":3,"start":53,"end":54},{"type":"T_LCURLY","context":"normal","value":"{","line":3,"start":54,"end":55},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":3,"start":55,"end":58},{"type":"T_COLON","context":"normal","value":":","line":3,"start":58,"end":59},{"type":"T_NUMBER","context":"normal","value":"12","line":3,"start":60,"end":62},{"type":"T_RCURLY","context":"normal","value":"}","line":3,"start":62,"end":63},{"type":"T_COMMA","context":"normal","value":",","line":3,"start":63,"end":64},{"type":"T_LCURLY","context":"normal","value":"{","line":3,"start":65,"end":66},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":3,"start":66,"end":69},{"type":"T_COLON","context":"normal","value":":","line":3,"start":69,"end":70},{"type":"T_NUMBER","context":"normal","value":"18","line":3,"start":71,"end":73},{"type":"T_RCURLY","context":"normal","value":"}","line":3,"start":73,"end":74},{"type":"T_COMMA","context":"normal","value":",","line":3,"start":74,"end":75},{"type":"T_LCURLY","context":"normal","value":"{","line":3,"start":76,"end":77},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":3,"start":77,"end":80},{"type":"T_COLON","context":"normal","value":":","line":3,"start":80,"end":81},{"type":"T_NUMBER","context":"normal","value":"24","line":3,"start":82,"end":84},{"type":"T_RCURLY","context":"normal","value":"}","line":3,"start":84,"end":85},{"type":"T_RBRACKET","context":"normal","value":"]","line":3,"start":85,"end":86},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":86,"end":87},{"type":"T_CONST","context":"normal","value":"const","line":4,"start":88,"end":93},{"type":"T_IDENTIFIER","context":"normal","value":"oldPerson","line":4,"start":94,"end":103},{"type":"T_COLON","context":"type","value":":","line":4,"start":103,"end":104},{"type":"T_IDENTIFIER","context":"type","value":"Person","line":4,"start":105,"end":111},{"type":"T_ASSIGN","context":"normal","value":"=","line":4,"start":112,"end":113},{"type":"T_LCURLY","context":"normal","value":"{","line":4,"start":114,"end":115},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":4,"start":115,"end":118},{"type":"T_COLON","context":"normal","value":":","line":4,"start":118,"end":119},{"type":"T_NUMBER","context":"normal","value":"70","line":4,"start":120,"end":122},{"type":"T_RCURLY","context":"normal","value":"}","line":4,"start":122,"end":123},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":123,"end":124},{"type":"T_IF","context":"normal","value":"if","line":5,"start":125,"end":127},{"type":"T_LPAREN","context":"normal","value":"(","line":5,"start":128,"end":129},{"type":"T_IDENTIFIER","context":"normal","value":"oldPerson","line":5,"start":129,"end":138},{"type":"T_PERIOD","context":"normal","value":".","line":5,"start":138,"end":139},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":5,"start":139,"end":142},{"type":"T_RPAREN","context":"normal","value":")","line":5,"start":142,"end":143},{"type":"T_LCURLY","context":"normal","value":"{","line":5,"start":144,"end":145},{"type":"T_IDENTIFIER","context":"normal","value":"people","line":6,"start":148,"end":154},{"type":"T_PERIOD","context":"normal","value":".","line":6,"start":154,"end":155},{"type":"T_IDENTIFIER","context":"normal","value":"forEach","line":6,"start":155,"end":162},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":162,"end":163},{"type":"T_IDENTIFIER","context":"normal","value":"person","line":6,"start":163,"end":169},{"type":"T_ARROW","context":"normal","value":"=>","line":6,"start":170,"end":172},{"type":"T_LCURLY","context":"normal","value":"{","line":6,"start":173,"end":174},{"type":"T_IDENTIFIER","context":"normal","value":"console","line":7,"start":179,"end":186},{"type":"T_PERIOD","context":"normal","value":".","line":7,"start":186,"end":187},{"type":"T_IDENTIFIER","context":"normal","value":"log","line":7,"start":187,"end":190},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":190,"end":191},{"type":"T_TEMPLATE_PART","context":"normal","value":"`The person is ${","line":7,"start":191,"end":208},{"type":"T_IDENTIFIER","context":"normal","value":"person","line":7,"start":208,"end":214},{"type":"T_PERIOD","context":"normal","value":".","line":7,"start":214,"end":215},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":7,"start":215,"end":218},{"type":"T_TEMPLATE_PART","context":"template","value":"} and the old one is ${","line":7,"start":218,"end":241},{"type":"T_IDENTIFIER","context":"normal","value":"oldPerson","line":7,"start":241,"end":250},{"type":"T_PERIOD","context":"normal","value":".","line":7,"start":250,"end":251},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":7,"start":251,"end":254},{"type":"T_TEMPLATE_PART","context":"template","value":"}`","line":7,"start":254,"end":256},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":256,"end":257},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":257,"end":258},{"type":"T_RCURLY","context":"normal","value":"}","line":8,"start":261,"end":262},{"type":"T_RPAREN","context":"normal","value":")","line":8,"start":262,"end":263},{"type":"T_RCURLY","context":"normal","value":"}","line":9,"start":264,"end":265}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot coerce `oldPerson.age` to string because null or undefined [1] should not be coerced. [incompatible-type]","context":" console.log(`The person is ${person.age} and the old one is ${oldPerson.age}`);","source":"-","start":{"line":7,"column":67,"offset":241},"end":{"line":7,"column":79,"offset":254}}],"operation":null}]}
The solution here is to move the if check in the forEach
, or to assign the age
to an intermediate variable.
Example (https://flow.org/try):
1
2
3
4
5
6
7
8
9
10
|
type Person = {age: ?number}
const people = [{age: 12}, {age: 18}, {age: 24}];
const oldPerson: Person = {age: 70};
if (oldPerson.age) {
const age = oldPerson.age;
people.forEach(person => {
console.log(`The person is ${person.age} and the old one is ${age}`);
})
}
|
{"value":"// @flow\ntype Person = {age: ?number}\nconst people = [{age: 12}, {age: 18}, {age: 24}];\nconst oldPerson: Person = {age: 70};\nif (oldPerson.age) {\n const age = oldPerson.age;\n people.forEach(person => {\n console.log(`The person is ${person.age} and the old one is ${age}`);\n })\n}\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_TYPE","context":"normal","value":"type","line":2,"start":9,"end":13},{"type":"T_IDENTIFIER","context":"type","value":"Person","line":2,"start":14,"end":20},{"type":"T_ASSIGN","context":"type","value":"=","line":2,"start":21,"end":22},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":23,"end":24},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":2,"start":24,"end":27},{"type":"T_COLON","context":"type","value":":","line":2,"start":27,"end":28},{"type":"T_PLING","context":"type","value":"?","line":2,"start":29,"end":30},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":2,"start":30,"end":36},{"type":"T_RCURLY","context":"type","value":"}","line":2,"start":36,"end":37},{"type":"T_CONST","context":"normal","value":"const","line":3,"start":38,"end":43},{"type":"T_IDENTIFIER","context":"normal","value":"people","line":3,"start":44,"end":50},{"type":"T_ASSIGN","context":"normal","value":"=","line":3,"start":51,"end":52},{"type":"T_LBRACKET","context":"normal","value":"[","line":3,"start":53,"end":54},{"type":"T_LCURLY","context":"normal","value":"{","line":3,"start":54,"end":55},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":3,"start":55,"end":58},{"type":"T_COLON","context":"normal","value":":","line":3,"start":58,"end":59},{"type":"T_NUMBER","context":"normal","value":"12","line":3,"start":60,"end":62},{"type":"T_RCURLY","context":"normal","value":"}","line":3,"start":62,"end":63},{"type":"T_COMMA","context":"normal","value":",","line":3,"start":63,"end":64},{"type":"T_LCURLY","context":"normal","value":"{","line":3,"start":65,"end":66},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":3,"start":66,"end":69},{"type":"T_COLON","context":"normal","value":":","line":3,"start":69,"end":70},{"type":"T_NUMBER","context":"normal","value":"18","line":3,"start":71,"end":73},{"type":"T_RCURLY","context":"normal","value":"}","line":3,"start":73,"end":74},{"type":"T_COMMA","context":"normal","value":",","line":3,"start":74,"end":75},{"type":"T_LCURLY","context":"normal","value":"{","line":3,"start":76,"end":77},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":3,"start":77,"end":80},{"type":"T_COLON","context":"normal","value":":","line":3,"start":80,"end":81},{"type":"T_NUMBER","context":"normal","value":"24","line":3,"start":82,"end":84},{"type":"T_RCURLY","context":"normal","value":"}","line":3,"start":84,"end":85},{"type":"T_RBRACKET","context":"normal","value":"]","line":3,"start":85,"end":86},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":86,"end":87},{"type":"T_CONST","context":"normal","value":"const","line":4,"start":88,"end":93},{"type":"T_IDENTIFIER","context":"normal","value":"oldPerson","line":4,"start":94,"end":103},{"type":"T_COLON","context":"type","value":":","line":4,"start":103,"end":104},{"type":"T_IDENTIFIER","context":"type","value":"Person","line":4,"start":105,"end":111},{"type":"T_ASSIGN","context":"normal","value":"=","line":4,"start":112,"end":113},{"type":"T_LCURLY","context":"normal","value":"{","line":4,"start":114,"end":115},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":4,"start":115,"end":118},{"type":"T_COLON","context":"normal","value":":","line":4,"start":118,"end":119},{"type":"T_NUMBER","context":"normal","value":"70","line":4,"start":120,"end":122},{"type":"T_RCURLY","context":"normal","value":"}","line":4,"start":122,"end":123},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":123,"end":124},{"type":"T_IF","context":"normal","value":"if","line":5,"start":125,"end":127},{"type":"T_LPAREN","context":"normal","value":"(","line":5,"start":128,"end":129},{"type":"T_IDENTIFIER","context":"normal","value":"oldPerson","line":5,"start":129,"end":138},{"type":"T_PERIOD","context":"normal","value":".","line":5,"start":138,"end":139},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":5,"start":139,"end":142},{"type":"T_RPAREN","context":"normal","value":")","line":5,"start":142,"end":143},{"type":"T_LCURLY","context":"normal","value":"{","line":5,"start":144,"end":145},{"type":"T_CONST","context":"normal","value":"const","line":6,"start":148,"end":153},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":6,"start":154,"end":157},{"type":"T_ASSIGN","context":"normal","value":"=","line":6,"start":158,"end":159},{"type":"T_IDENTIFIER","context":"normal","value":"oldPerson","line":6,"start":160,"end":169},{"type":"T_PERIOD","context":"normal","value":".","line":6,"start":169,"end":170},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":6,"start":170,"end":173},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":173,"end":174},{"type":"T_IDENTIFIER","context":"normal","value":"people","line":7,"start":177,"end":183},{"type":"T_PERIOD","context":"normal","value":".","line":7,"start":183,"end":184},{"type":"T_IDENTIFIER","context":"normal","value":"forEach","line":7,"start":184,"end":191},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":191,"end":192},{"type":"T_IDENTIFIER","context":"normal","value":"person","line":7,"start":192,"end":198},{"type":"T_ARROW","context":"normal","value":"=>","line":7,"start":199,"end":201},{"type":"T_LCURLY","context":"normal","value":"{","line":7,"start":202,"end":203},{"type":"T_IDENTIFIER","context":"normal","value":"console","line":8,"start":208,"end":215},{"type":"T_PERIOD","context":"normal","value":".","line":8,"start":215,"end":216},{"type":"T_IDENTIFIER","context":"normal","value":"log","line":8,"start":216,"end":219},{"type":"T_LPAREN","context":"normal","value":"(","line":8,"start":219,"end":220},{"type":"T_TEMPLATE_PART","context":"normal","value":"`The person is ${","line":8,"start":220,"end":237},{"type":"T_IDENTIFIER","context":"normal","value":"person","line":8,"start":237,"end":243},{"type":"T_PERIOD","context":"normal","value":".","line":8,"start":243,"end":244},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":8,"start":244,"end":247},{"type":"T_TEMPLATE_PART","context":"template","value":"} and the old one is ${","line":8,"start":247,"end":270},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":8,"start":270,"end":273},{"type":"T_TEMPLATE_PART","context":"template","value":"}`","line":8,"start":273,"end":275},{"type":"T_RPAREN","context":"normal","value":")","line":8,"start":275,"end":276},{"type":"T_SEMICOLON","context":"normal","value":";","line":8,"start":276,"end":277},{"type":"T_RCURLY","context":"normal","value":"}","line":9,"start":280,"end":281},{"type":"T_RPAREN","context":"normal","value":")","line":9,"start":281,"end":282},{"type":"T_RCURLY","context":"normal","value":"}","line":10,"start":283,"end":284}],"errors":[]}
But Flow should understand that this function cannot invalidate this refinement, right?
Flow is not complete, so it cannot check all code perfectly. Instead,
Flow will make conservative assumptions to try to be sound.
Why can’t I use a function in my if-clause to check the type of a property?
Flow doesn’t track refinements made in separated function calls.
Example (https://flow.org/try)
1
2
3
4
5
|
const add = (first: number, second: number) => first + second;
const val: string | number = ...
const isNumber = (valueToRefine: ?number) => typeof valueToRefine === 'number';
if (isNumber(val)) add(val, 2);
|
Unexpected token `...`
{"value":"// @flow\nconst add = (first: number, second: number) => first + second;\nconst val: string | number = ...\nconst isNumber = (valueToRefine: ?number) => typeof valueToRefine === 'number';\nif (isNumber(val)) add(val, 2);\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_CONST","context":"normal","value":"const","line":2,"start":9,"end":14},{"type":"T_IDENTIFIER","context":"normal","value":"add","line":2,"start":15,"end":18},{"type":"T_ASSIGN","context":"normal","value":"=","line":2,"start":19,"end":20},{"type":"T_LPAREN","context":"normal","value":"(","line":2,"start":21,"end":22},{"type":"T_IDENTIFIER","context":"normal","value":"first","line":2,"start":22,"end":27},{"type":"T_COLON","context":"type","value":":","line":2,"start":27,"end":28},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":2,"start":29,"end":35},{"type":"T_COMMA","context":"normal","value":",","line":2,"start":35,"end":36},{"type":"T_IDENTIFIER","context":"normal","value":"second","line":2,"start":37,"end":43},{"type":"T_COLON","context":"type","value":":","line":2,"start":43,"end":44},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":2,"start":45,"end":51},{"type":"T_RPAREN","context":"normal","value":")","line":2,"start":51,"end":52},{"type":"T_ARROW","context":"normal","value":"=>","line":2,"start":53,"end":55},{"type":"T_IDENTIFIER","context":"normal","value":"first","line":2,"start":56,"end":61},{"type":"T_PLUS","context":"normal","value":"+","line":2,"start":62,"end":63},{"type":"T_IDENTIFIER","context":"normal","value":"second","line":2,"start":64,"end":70},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":70,"end":71},{"type":"T_CONST","context":"normal","value":"const","line":3,"start":72,"end":77},{"type":"T_IDENTIFIER","context":"normal","value":"val","line":3,"start":78,"end":81},{"type":"T_COLON","context":"type","value":":","line":3,"start":81,"end":82},{"type":"T_STRING_TYPE","context":"type","value":"string","line":3,"start":83,"end":89},{"type":"T_BIT_OR","context":"type","value":"|","line":3,"start":90,"end":91},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":3,"start":92,"end":98},{"type":"T_ASSIGN","context":"normal","value":"=","line":3,"start":99,"end":100},{"type":"T_ELLIPSIS","context":"normal","value":"...","line":3,"start":101,"end":104},{"type":"T_CONST","context":"normal","value":"const","line":4,"start":105,"end":110},{"type":"T_IDENTIFIER","context":"normal","value":"isNumber","line":4,"start":111,"end":119},{"type":"T_ASSIGN","context":"normal","value":"=","line":4,"start":120,"end":121},{"type":"T_LPAREN","context":"normal","value":"(","line":4,"start":122,"end":123},{"type":"T_IDENTIFIER","context":"normal","value":"valueToRefine","line":4,"start":123,"end":136},{"type":"T_COLON","context":"type","value":":","line":4,"start":136,"end":137},{"type":"T_PLING","context":"type","value":"?","line":4,"start":138,"end":139},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":4,"start":139,"end":145},{"type":"T_RPAREN","context":"normal","value":")","line":4,"start":145,"end":146},{"type":"T_ARROW","context":"normal","value":"=>","line":4,"start":147,"end":149},{"type":"T_TYPEOF","context":"normal","value":"typeof","line":4,"start":150,"end":156},{"type":"T_IDENTIFIER","context":"normal","value":"valueToRefine","line":4,"start":157,"end":170},{"type":"T_STRICT_EQUAL","context":"normal","value":"===","line":4,"start":171,"end":174},{"type":"T_STRING","context":"normal","value":"'number'","line":4,"start":175,"end":183},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":183,"end":184},{"type":"T_IF","context":"normal","value":"if","line":5,"start":185,"end":187},{"type":"T_LPAREN","context":"normal","value":"(","line":5,"start":188,"end":189},{"type":"T_IDENTIFIER","context":"normal","value":"isNumber","line":5,"start":189,"end":197},{"type":"T_LPAREN","context":"normal","value":"(","line":5,"start":197,"end":198},{"type":"T_IDENTIFIER","context":"normal","value":"val","line":5,"start":198,"end":201},{"type":"T_RPAREN","context":"normal","value":")","line":5,"start":201,"end":202},{"type":"T_RPAREN","context":"normal","value":")","line":5,"start":202,"end":203},{"type":"T_IDENTIFIER","context":"normal","value":"add","line":5,"start":204,"end":207},{"type":"T_LPAREN","context":"normal","value":"(","line":5,"start":207,"end":208},{"type":"T_IDENTIFIER","context":"normal","value":"val","line":5,"start":208,"end":211},{"type":"T_COMMA","context":"normal","value":",","line":5,"start":211,"end":212},{"type":"T_NUMBER","context":"normal","value":"2","line":5,"start":213,"end":214},{"type":"T_RPAREN","context":"normal","value":")","line":5,"start":214,"end":215},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":215,"end":216}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Unexpected token `...`","context":"const val: string | number = ...","source":"-","start":{"line":3,"column":30,"offset":101},"end":{"line":3,"column":32,"offset":104}}],"operation":null}]}
However, Flow has predicates functions that can do these checks via %checks
.
Example (https://flow.org/try)
1
2
3
4
5
|
const add = (first: number, second: number) => first + second;
const val: string | number = ...
const isNumber = (valueToRefine: ?number): %checks => typeof valueToRefine === 'number';
if (isNumber(val)) add(val, 2);
|
Unexpected token `...`
{"value":"// @flow\nconst add = (first: number, second: number) => first + second;\nconst val: string | number = ...\nconst isNumber = (valueToRefine: ?number): %checks => typeof valueToRefine === 'number';\nif (isNumber(val)) add(val, 2);\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_CONST","context":"normal","value":"const","line":2,"start":9,"end":14},{"type":"T_IDENTIFIER","context":"normal","value":"add","line":2,"start":15,"end":18},{"type":"T_ASSIGN","context":"normal","value":"=","line":2,"start":19,"end":20},{"type":"T_LPAREN","context":"normal","value":"(","line":2,"start":21,"end":22},{"type":"T_IDENTIFIER","context":"normal","value":"first","line":2,"start":22,"end":27},{"type":"T_COLON","context":"type","value":":","line":2,"start":27,"end":28},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":2,"start":29,"end":35},{"type":"T_COMMA","context":"normal","value":",","line":2,"start":35,"end":36},{"type":"T_IDENTIFIER","context":"normal","value":"second","line":2,"start":37,"end":43},{"type":"T_COLON","context":"type","value":":","line":2,"start":43,"end":44},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":2,"start":45,"end":51},{"type":"T_RPAREN","context":"normal","value":")","line":2,"start":51,"end":52},{"type":"T_ARROW","context":"normal","value":"=>","line":2,"start":53,"end":55},{"type":"T_IDENTIFIER","context":"normal","value":"first","line":2,"start":56,"end":61},{"type":"T_PLUS","context":"normal","value":"+","line":2,"start":62,"end":63},{"type":"T_IDENTIFIER","context":"normal","value":"second","line":2,"start":64,"end":70},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":70,"end":71},{"type":"T_CONST","context":"normal","value":"const","line":3,"start":72,"end":77},{"type":"T_IDENTIFIER","context":"normal","value":"val","line":3,"start":78,"end":81},{"type":"T_COLON","context":"type","value":":","line":3,"start":81,"end":82},{"type":"T_STRING_TYPE","context":"type","value":"string","line":3,"start":83,"end":89},{"type":"T_BIT_OR","context":"type","value":"|","line":3,"start":90,"end":91},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":3,"start":92,"end":98},{"type":"T_ASSIGN","context":"normal","value":"=","line":3,"start":99,"end":100},{"type":"T_ELLIPSIS","context":"normal","value":"...","line":3,"start":101,"end":104},{"type":"T_CONST","context":"normal","value":"const","line":4,"start":105,"end":110},{"type":"T_IDENTIFIER","context":"normal","value":"isNumber","line":4,"start":111,"end":119},{"type":"T_ASSIGN","context":"normal","value":"=","line":4,"start":120,"end":121},{"type":"T_LPAREN","context":"normal","value":"(","line":4,"start":122,"end":123},{"type":"T_IDENTIFIER","context":"normal","value":"valueToRefine","line":4,"start":123,"end":136},{"type":"T_COLON","context":"type","value":":","line":4,"start":136,"end":137},{"type":"T_PLING","context":"type","value":"?","line":4,"start":138,"end":139},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":4,"start":139,"end":145},{"type":"T_RPAREN","context":"normal","value":")","line":4,"start":145,"end":146},{"type":"T_COLON","context":"type","value":":","line":4,"start":146,"end":147},{"type":"T_CHECKS","context":"type","value":"%checks","line":4,"start":148,"end":155},{"type":"T_ARROW","context":"normal","value":"=>","line":4,"start":156,"end":158},{"type":"T_TYPEOF","context":"normal","value":"typeof","line":4,"start":159,"end":165},{"type":"T_IDENTIFIER","context":"normal","value":"valueToRefine","line":4,"start":166,"end":179},{"type":"T_STRICT_EQUAL","context":"normal","value":"===","line":4,"start":180,"end":183},{"type":"T_STRING","context":"normal","value":"'number'","line":4,"start":184,"end":192},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":192,"end":193},{"type":"T_IF","context":"normal","value":"if","line":5,"start":194,"end":196},{"type":"T_LPAREN","context":"normal","value":"(","line":5,"start":197,"end":198},{"type":"T_IDENTIFIER","context":"normal","value":"isNumber","line":5,"start":198,"end":206},{"type":"T_LPAREN","context":"normal","value":"(","line":5,"start":206,"end":207},{"type":"T_IDENTIFIER","context":"normal","value":"val","line":5,"start":207,"end":210},{"type":"T_RPAREN","context":"normal","value":")","line":5,"start":210,"end":211},{"type":"T_RPAREN","context":"normal","value":")","line":5,"start":211,"end":212},{"type":"T_IDENTIFIER","context":"normal","value":"add","line":5,"start":213,"end":216},{"type":"T_LPAREN","context":"normal","value":"(","line":5,"start":216,"end":217},{"type":"T_IDENTIFIER","context":"normal","value":"val","line":5,"start":217,"end":220},{"type":"T_COMMA","context":"normal","value":",","line":5,"start":220,"end":221},{"type":"T_NUMBER","context":"normal","value":"2","line":5,"start":222,"end":223},{"type":"T_RPAREN","context":"normal","value":")","line":5,"start":223,"end":224},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":224,"end":225}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Unexpected token `...`","context":"const val: string | number = ...","source":"-","start":{"line":3,"column":30,"offset":101},"end":{"line":3,"column":32,"offset":104}}],"operation":null}]}
Why can’t I pass an Array<string>
to a function that takes an Array<string | number>
The function’s argument allows string
values in its array, but in this case Flow prevents the original array from receiving a number
. Inside the function, you would be able to push a number
to the argument array, causing the type of the original array to no longer be accurate. You can fix this error by changing the type of the argument to $ReadOnlyArray<string | number>
. This prevents the function body from pushing anything to the array, allowing it to accept narrower types.
As an example, this would not work:
1
2
3
4
5
6
7
8
9
10
|
const fn = (arr: Array<string | number>) => {
return arr;
};
const arr: Array<string> = ['abc'];
fn(arr);
|
Cannot call `fn` with `arr` bound to `arr` because string [1] is incompatible with number [2] in array element. [incompatible-call]
{"value":"// @flow\n\nconst fn = (arr: Array<string | number>) => {\n // arr.push(123) NOTE! Array<string> passed in and after this it would also include numbers if allowed\n return arr;\n};\n\nconst arr: Array<string> = ['abc'];\n\nfn(arr); // Error!\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_CONST","context":"normal","value":"const","line":3,"start":10,"end":15},{"type":"T_IDENTIFIER","context":"normal","value":"fn","line":3,"start":16,"end":18},{"type":"T_ASSIGN","context":"normal","value":"=","line":3,"start":19,"end":20},{"type":"T_LPAREN","context":"normal","value":"(","line":3,"start":21,"end":22},{"type":"T_IDENTIFIER","context":"normal","value":"arr","line":3,"start":22,"end":25},{"type":"T_COLON","context":"type","value":":","line":3,"start":25,"end":26},{"type":"T_IDENTIFIER","context":"type","value":"Array","line":3,"start":27,"end":32},{"type":"T_LESS_THAN","context":"type","value":"<","line":3,"start":32,"end":33},{"type":"T_STRING_TYPE","context":"type","value":"string","line":3,"start":33,"end":39},{"type":"T_BIT_OR","context":"type","value":"|","line":3,"start":40,"end":41},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":3,"start":42,"end":48},{"type":"T_GREATER_THAN","context":"type","value":">","line":3,"start":48,"end":49},{"type":"T_RPAREN","context":"normal","value":")","line":3,"start":49,"end":50},{"type":"T_ARROW","context":"normal","value":"=>","line":3,"start":51,"end":53},{"type":"T_LCURLY","context":"normal","value":"{","line":3,"start":54,"end":55},{"type":"Line","context":"comment","value":"// arr.push(123) NOTE! Array<string> passed in and after this it would also include numbers if allowed","line":4,"start":58,"end":160},{"type":"T_RETURN","context":"normal","value":"return","line":5,"start":163,"end":169},{"type":"T_IDENTIFIER","context":"normal","value":"arr","line":5,"start":170,"end":173},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":173,"end":174},{"type":"T_RCURLY","context":"normal","value":"}","line":6,"start":175,"end":176},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":176,"end":177},{"type":"T_CONST","context":"normal","value":"const","line":8,"start":179,"end":184},{"type":"T_IDENTIFIER","context":"normal","value":"arr","line":8,"start":185,"end":188},{"type":"T_COLON","context":"type","value":":","line":8,"start":188,"end":189},{"type":"T_IDENTIFIER","context":"type","value":"Array","line":8,"start":190,"end":195},{"type":"T_LESS_THAN","context":"type","value":"<","line":8,"start":195,"end":196},{"type":"T_STRING_TYPE","context":"type","value":"string","line":8,"start":196,"end":202},{"type":"T_GREATER_THAN","context":"type","value":">","line":8,"start":202,"end":203},{"type":"T_ASSIGN","context":"normal","value":"=","line":8,"start":204,"end":205},{"type":"T_LBRACKET","context":"normal","value":"[","line":8,"start":206,"end":207},{"type":"T_STRING","context":"normal","value":"'abc'","line":8,"start":207,"end":212},{"type":"T_RBRACKET","context":"normal","value":"]","line":8,"start":212,"end":213},{"type":"T_SEMICOLON","context":"normal","value":";","line":8,"start":213,"end":214},{"type":"T_IDENTIFIER","context":"normal","value":"fn","line":10,"start":216,"end":218},{"type":"T_LPAREN","context":"normal","value":"(","line":10,"start":218,"end":219},{"type":"T_IDENTIFIER","context":"normal","value":"arr","line":10,"start":219,"end":222},{"type":"T_RPAREN","context":"normal","value":")","line":10,"start":222,"end":223},{"type":"T_SEMICOLON","context":"normal","value":";","line":10,"start":223,"end":224},{"type":"Line","context":"comment","value":"// Error!","line":10,"start":225,"end":234}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot call `fn` with `arr` bound to `arr` because string [1] is incompatible with number [2] in array element. [incompatible-call]","context":"fn(arr); // Error!","source":"-","start":{"line":10,"column":4,"offset":219},"end":{"line":10,"column":6,"offset":222}}],"operation":null}]}
but with $ReadOnlyArray
you can achieve what you were looking for:
1
2
3
4
5
6
7
8
9
|
const fn = (arr: $ReadOnlyArray<string | number>) => {
return arr;
};
const arr: Array<string> = ['abc'];
fn(arr);
|
{"value":"// @flow\nconst fn = (arr: $ReadOnlyArray<string | number>) => {\n // arr.push(321) NOTE! Since you are using $ReadOnlyArray<...> you cannot push anything to it\n return arr;\n};\n\nconst arr: Array<string> = ['abc'];\n\nfn(arr);\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_CONST","context":"normal","value":"const","line":2,"start":9,"end":14},{"type":"T_IDENTIFIER","context":"normal","value":"fn","line":2,"start":15,"end":17},{"type":"T_ASSIGN","context":"normal","value":"=","line":2,"start":18,"end":19},{"type":"T_LPAREN","context":"normal","value":"(","line":2,"start":20,"end":21},{"type":"T_IDENTIFIER","context":"normal","value":"arr","line":2,"start":21,"end":24},{"type":"T_COLON","context":"type","value":":","line":2,"start":24,"end":25},{"type":"T_IDENTIFIER","context":"type","value":"$ReadOnlyArray","line":2,"start":26,"end":40},{"type":"T_LESS_THAN","context":"type","value":"<","line":2,"start":40,"end":41},{"type":"T_STRING_TYPE","context":"type","value":"string","line":2,"start":41,"end":47},{"type":"T_BIT_OR","context":"type","value":"|","line":2,"start":48,"end":49},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":2,"start":50,"end":56},{"type":"T_GREATER_THAN","context":"type","value":">","line":2,"start":56,"end":57},{"type":"T_RPAREN","context":"normal","value":")","line":2,"start":57,"end":58},{"type":"T_ARROW","context":"normal","value":"=>","line":2,"start":59,"end":61},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":62,"end":63},{"type":"Line","context":"comment","value":"// arr.push(321) NOTE! Since you are using $ReadOnlyArray<...> you cannot push anything to it","line":3,"start":66,"end":159},{"type":"T_RETURN","context":"normal","value":"return","line":4,"start":162,"end":168},{"type":"T_IDENTIFIER","context":"normal","value":"arr","line":4,"start":169,"end":172},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":172,"end":173},{"type":"T_RCURLY","context":"normal","value":"}","line":5,"start":174,"end":175},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":175,"end":176},{"type":"T_CONST","context":"normal","value":"const","line":7,"start":178,"end":183},{"type":"T_IDENTIFIER","context":"normal","value":"arr","line":7,"start":184,"end":187},{"type":"T_COLON","context":"type","value":":","line":7,"start":187,"end":188},{"type":"T_IDENTIFIER","context":"type","value":"Array","line":7,"start":189,"end":194},{"type":"T_LESS_THAN","context":"type","value":"<","line":7,"start":194,"end":195},{"type":"T_STRING_TYPE","context":"type","value":"string","line":7,"start":195,"end":201},{"type":"T_GREATER_THAN","context":"type","value":">","line":7,"start":201,"end":202},{"type":"T_ASSIGN","context":"normal","value":"=","line":7,"start":203,"end":204},{"type":"T_LBRACKET","context":"normal","value":"[","line":7,"start":205,"end":206},{"type":"T_STRING","context":"normal","value":"'abc'","line":7,"start":206,"end":211},{"type":"T_RBRACKET","context":"normal","value":"]","line":7,"start":211,"end":212},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":212,"end":213},{"type":"T_IDENTIFIER","context":"normal","value":"fn","line":9,"start":215,"end":217},{"type":"T_LPAREN","context":"normal","value":"(","line":9,"start":217,"end":218},{"type":"T_IDENTIFIER","context":"normal","value":"arr","line":9,"start":218,"end":221},{"type":"T_RPAREN","context":"normal","value":")","line":9,"start":221,"end":222},{"type":"T_SEMICOLON","context":"normal","value":";","line":9,"start":222,"end":223}],"errors":[]}
Example (https://flow.org/try)
Why can’t I pass { a: string }
to a function that takes { a: string | number }
The function argument allows string
values in its field, but in this case Flow prevents the original object from having a number
written to it. Within the body of the function you would be able to mutate the object so that the property a
would receive a number
, causing the type of the original object to no longer be accurate. You can fix this error by making the property covariant (read-only): { +a: string | number }
. This prevents the function body from writing to the property, making it safe to pass more restricted types to the function.
As an example, this would not work:
1
2
3
4
5
6
7
8
9
10
|
const fn = (obj: {| a: string | number |}) => {
return obj;
};
const object: {| a: string |} = {a: 'str' };
fn(object);
|
Cannot call `fn` with `object` bound to `obj` because string [1] is incompatible with number [2] in property `a`. [incompatible-call]
{"value":"// @flow\n\nconst fn = (obj: {| a: string | number |}) => {\n // obj.a = 123;\n return obj;\n};\n\nconst object: {| a: string |} = {a: 'str' };\n\nfn(object); // Error!\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_CONST","context":"normal","value":"const","line":3,"start":10,"end":15},{"type":"T_IDENTIFIER","context":"normal","value":"fn","line":3,"start":16,"end":18},{"type":"T_ASSIGN","context":"normal","value":"=","line":3,"start":19,"end":20},{"type":"T_LPAREN","context":"normal","value":"(","line":3,"start":21,"end":22},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":3,"start":22,"end":25},{"type":"T_COLON","context":"type","value":":","line":3,"start":25,"end":26},{"type":"T_LCURLYBAR","context":"type","value":"{|","line":3,"start":27,"end":29},{"type":"T_IDENTIFIER","context":"normal","value":"a","line":3,"start":30,"end":31},{"type":"T_COLON","context":"type","value":":","line":3,"start":31,"end":32},{"type":"T_STRING_TYPE","context":"type","value":"string","line":3,"start":33,"end":39},{"type":"T_BIT_OR","context":"type","value":"|","line":3,"start":40,"end":41},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":3,"start":42,"end":48},{"type":"T_RCURLYBAR","context":"type","value":"|}","line":3,"start":49,"end":51},{"type":"T_RPAREN","context":"normal","value":")","line":3,"start":51,"end":52},{"type":"T_ARROW","context":"normal","value":"=>","line":3,"start":53,"end":55},{"type":"T_LCURLY","context":"normal","value":"{","line":3,"start":56,"end":57},{"type":"Line","context":"comment","value":"// obj.a = 123;","line":4,"start":60,"end":75},{"type":"T_RETURN","context":"normal","value":"return","line":5,"start":78,"end":84},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":5,"start":85,"end":88},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":88,"end":89},{"type":"T_RCURLY","context":"normal","value":"}","line":6,"start":90,"end":91},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":91,"end":92},{"type":"T_CONST","context":"normal","value":"const","line":8,"start":94,"end":99},{"type":"T_IDENTIFIER","context":"normal","value":"object","line":8,"start":100,"end":106},{"type":"T_COLON","context":"type","value":":","line":8,"start":106,"end":107},{"type":"T_LCURLYBAR","context":"type","value":"{|","line":8,"start":108,"end":110},{"type":"T_IDENTIFIER","context":"normal","value":"a","line":8,"start":111,"end":112},{"type":"T_COLON","context":"type","value":":","line":8,"start":112,"end":113},{"type":"T_STRING_TYPE","context":"type","value":"string","line":8,"start":114,"end":120},{"type":"T_RCURLYBAR","context":"type","value":"|}","line":8,"start":121,"end":123},{"type":"T_ASSIGN","context":"normal","value":"=","line":8,"start":124,"end":125},{"type":"T_LCURLY","context":"normal","value":"{","line":8,"start":126,"end":127},{"type":"T_IDENTIFIER","context":"normal","value":"a","line":8,"start":127,"end":128},{"type":"T_COLON","context":"normal","value":":","line":8,"start":128,"end":129},{"type":"T_STRING","context":"normal","value":"'str'","line":8,"start":130,"end":135},{"type":"T_RCURLY","context":"normal","value":"}","line":8,"start":136,"end":137},{"type":"T_SEMICOLON","context":"normal","value":";","line":8,"start":137,"end":138},{"type":"T_IDENTIFIER","context":"normal","value":"fn","line":10,"start":140,"end":142},{"type":"T_LPAREN","context":"normal","value":"(","line":10,"start":142,"end":143},{"type":"T_IDENTIFIER","context":"normal","value":"object","line":10,"start":143,"end":149},{"type":"T_RPAREN","context":"normal","value":")","line":10,"start":149,"end":150},{"type":"T_SEMICOLON","context":"normal","value":";","line":10,"start":150,"end":151},{"type":"Line","context":"comment","value":"// Error!","line":10,"start":152,"end":161}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot call `fn` with `object` bound to `obj` because string [1] is incompatible with number [2] in property `a`. [incompatible-call]","context":"fn(object); // Error!","source":"-","start":{"line":10,"column":4,"offset":143},"end":{"line":10,"column":9,"offset":149}}],"operation":null}]}
but with a covariant property you can achieve what you were looking for:
1
2
3
4
5
6
7
8
9
|
const fn = (obj: {| +a: string | number |}) => {
return obj;
};
const object: {| a: string |} = { a: 'str' };
fn(object);
|
{"value":"// @flow\nconst fn = (obj: {| +a: string | number |}) => {\n // obj.a = 123 NOTE! Since you are using covariant {| +a: string | number |}, you can't mutate it\n return obj;\n};\n\nconst object: {| a: string |} = { a: 'str' };\n\nfn(object);\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_CONST","context":"normal","value":"const","line":2,"start":9,"end":14},{"type":"T_IDENTIFIER","context":"normal","value":"fn","line":2,"start":15,"end":17},{"type":"T_ASSIGN","context":"normal","value":"=","line":2,"start":18,"end":19},{"type":"T_LPAREN","context":"normal","value":"(","line":2,"start":20,"end":21},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":2,"start":21,"end":24},{"type":"T_COLON","context":"type","value":":","line":2,"start":24,"end":25},{"type":"T_LCURLYBAR","context":"type","value":"{|","line":2,"start":26,"end":28},{"type":"T_PLUS","context":"type","value":"+","line":2,"start":29,"end":30},{"type":"T_IDENTIFIER","context":"normal","value":"a","line":2,"start":30,"end":31},{"type":"T_COLON","context":"type","value":":","line":2,"start":31,"end":32},{"type":"T_STRING_TYPE","context":"type","value":"string","line":2,"start":33,"end":39},{"type":"T_BIT_OR","context":"type","value":"|","line":2,"start":40,"end":41},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":2,"start":42,"end":48},{"type":"T_RCURLYBAR","context":"type","value":"|}","line":2,"start":49,"end":51},{"type":"T_RPAREN","context":"normal","value":")","line":2,"start":51,"end":52},{"type":"T_ARROW","context":"normal","value":"=>","line":2,"start":53,"end":55},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":56,"end":57},{"type":"Line","context":"comment","value":"// obj.a = 123 NOTE! Since you are using covariant {| +a: string | number |}, you can't mutate it","line":3,"start":60,"end":157},{"type":"T_RETURN","context":"normal","value":"return","line":4,"start":160,"end":166},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":4,"start":167,"end":170},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":170,"end":171},{"type":"T_RCURLY","context":"normal","value":"}","line":5,"start":172,"end":173},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":173,"end":174},{"type":"T_CONST","context":"normal","value":"const","line":7,"start":176,"end":181},{"type":"T_IDENTIFIER","context":"normal","value":"object","line":7,"start":182,"end":188},{"type":"T_COLON","context":"type","value":":","line":7,"start":188,"end":189},{"type":"T_LCURLYBAR","context":"type","value":"{|","line":7,"start":190,"end":192},{"type":"T_IDENTIFIER","context":"normal","value":"a","line":7,"start":193,"end":194},{"type":"T_COLON","context":"type","value":":","line":7,"start":194,"end":195},{"type":"T_STRING_TYPE","context":"type","value":"string","line":7,"start":196,"end":202},{"type":"T_RCURLYBAR","context":"type","value":"|}","line":7,"start":203,"end":205},{"type":"T_ASSIGN","context":"normal","value":"=","line":7,"start":206,"end":207},{"type":"T_LCURLY","context":"normal","value":"{","line":7,"start":208,"end":209},{"type":"T_IDENTIFIER","context":"normal","value":"a","line":7,"start":210,"end":211},{"type":"T_COLON","context":"normal","value":":","line":7,"start":211,"end":212},{"type":"T_STRING","context":"normal","value":"'str'","line":7,"start":213,"end":218},{"type":"T_RCURLY","context":"normal","value":"}","line":7,"start":219,"end":220},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":220,"end":221},{"type":"T_IDENTIFIER","context":"normal","value":"fn","line":9,"start":223,"end":225},{"type":"T_LPAREN","context":"normal","value":"(","line":9,"start":225,"end":226},{"type":"T_IDENTIFIER","context":"normal","value":"object","line":9,"start":226,"end":232},{"type":"T_RPAREN","context":"normal","value":")","line":9,"start":232,"end":233},{"type":"T_SEMICOLON","context":"normal","value":";","line":9,"start":233,"end":234}],"errors":[]}
Example (https://flow.org/try)
Why can’t I refine a union of objects?
There are two potential reasons:
- You are using inexact objects.
- You are destructuring the object. When destructuring, Flow loses track of object properties.
Broken example:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
type Action =
| {type: 'A', payload: string}
| {type: 'B', payload: number};
const fn = ({type, payload}: Action) => {
switch (type) {
case 'A': return payload.length;
case 'B': return payload + 10;
}
}
|
Cannot get `payload.length` because property `length` is missing in `Number` [1]. [prop-missing]
{"value":"/* @flow */\n\ntype Action =\n | {type: 'A', payload: string}\n | {type: 'B', payload: number};\n\n// Not OK\nconst fn = ({type, payload}: Action) => {\n switch (type) {\n case 'A': return payload.length;\n case 'B': return payload + 10;\n }\n}\n","tokens":[{"type":"Block","context":"comment","value":"/* @flow */","line":1,"start":0,"end":11},{"type":"T_TYPE","context":"normal","value":"type","line":3,"start":13,"end":17},{"type":"T_IDENTIFIER","context":"type","value":"Action","line":3,"start":18,"end":24},{"type":"T_ASSIGN","context":"type","value":"=","line":3,"start":25,"end":26},{"type":"T_BIT_OR","context":"type","value":"|","line":4,"start":29,"end":30},{"type":"T_LCURLY","context":"type","value":"{","line":4,"start":31,"end":32},{"type":"T_TYPE","context":"normal","value":"type","line":4,"start":32,"end":36},{"type":"T_COLON","context":"type","value":":","line":4,"start":36,"end":37},{"type":"T_STRING","context":"type","value":"'A'","line":4,"start":38,"end":41},{"type":"T_COMMA","context":"type","value":",","line":4,"start":41,"end":42},{"type":"T_IDENTIFIER","context":"normal","value":"payload","line":4,"start":43,"end":50},{"type":"T_COLON","context":"type","value":":","line":4,"start":50,"end":51},{"type":"T_STRING_TYPE","context":"type","value":"string","line":4,"start":52,"end":58},{"type":"T_RCURLY","context":"type","value":"}","line":4,"start":58,"end":59},{"type":"T_BIT_OR","context":"type","value":"|","line":5,"start":62,"end":63},{"type":"T_LCURLY","context":"type","value":"{","line":5,"start":64,"end":65},{"type":"T_TYPE","context":"normal","value":"type","line":5,"start":65,"end":69},{"type":"T_COLON","context":"type","value":":","line":5,"start":69,"end":70},{"type":"T_STRING","context":"type","value":"'B'","line":5,"start":71,"end":74},{"type":"T_COMMA","context":"type","value":",","line":5,"start":74,"end":75},{"type":"T_IDENTIFIER","context":"normal","value":"payload","line":5,"start":76,"end":83},{"type":"T_COLON","context":"type","value":":","line":5,"start":83,"end":84},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":5,"start":85,"end":91},{"type":"T_RCURLY","context":"type","value":"}","line":5,"start":91,"end":92},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":92,"end":93},{"type":"Line","context":"comment","value":"// Not OK","line":7,"start":95,"end":104},{"type":"T_CONST","context":"normal","value":"const","line":8,"start":105,"end":110},{"type":"T_IDENTIFIER","context":"normal","value":"fn","line":8,"start":111,"end":113},{"type":"T_ASSIGN","context":"normal","value":"=","line":8,"start":114,"end":115},{"type":"T_LPAREN","context":"normal","value":"(","line":8,"start":116,"end":117},{"type":"T_LCURLY","context":"normal","value":"{","line":8,"start":117,"end":118},{"type":"T_TYPE","context":"normal","value":"type","line":8,"start":118,"end":122},{"type":"T_COMMA","context":"normal","value":",","line":8,"start":122,"end":123},{"type":"T_IDENTIFIER","context":"normal","value":"payload","line":8,"start":124,"end":131},{"type":"T_RCURLY","context":"normal","value":"}","line":8,"start":131,"end":132},{"type":"T_COLON","context":"type","value":":","line":8,"start":132,"end":133},{"type":"T_IDENTIFIER","context":"type","value":"Action","line":8,"start":134,"end":140},{"type":"T_RPAREN","context":"normal","value":")","line":8,"start":140,"end":141},{"type":"T_ARROW","context":"normal","value":"=>","line":8,"start":142,"end":144},{"type":"T_LCURLY","context":"normal","value":"{","line":8,"start":145,"end":146},{"type":"T_SWITCH","context":"normal","value":"switch","line":9,"start":149,"end":155},{"type":"T_LPAREN","context":"normal","value":"(","line":9,"start":156,"end":157},{"type":"T_TYPE","context":"normal","value":"type","line":9,"start":157,"end":161},{"type":"T_RPAREN","context":"normal","value":")","line":9,"start":161,"end":162},{"type":"T_LCURLY","context":"normal","value":"{","line":9,"start":163,"end":164},{"type":"T_CASE","context":"normal","value":"case","line":10,"start":169,"end":173},{"type":"T_STRING","context":"normal","value":"'A'","line":10,"start":174,"end":177},{"type":"T_COLON","context":"normal","value":":","line":10,"start":177,"end":178},{"type":"T_RETURN","context":"normal","value":"return","line":10,"start":179,"end":185},{"type":"T_IDENTIFIER","context":"normal","value":"payload","line":10,"start":186,"end":193},{"type":"T_PERIOD","context":"normal","value":".","line":10,"start":193,"end":194},{"type":"T_IDENTIFIER","context":"normal","value":"length","line":10,"start":194,"end":200},{"type":"T_SEMICOLON","context":"normal","value":";","line":10,"start":200,"end":201},{"type":"T_CASE","context":"normal","value":"case","line":11,"start":206,"end":210},{"type":"T_STRING","context":"normal","value":"'B'","line":11,"start":211,"end":214},{"type":"T_COLON","context":"normal","value":":","line":11,"start":214,"end":215},{"type":"T_RETURN","context":"normal","value":"return","line":11,"start":216,"end":222},{"type":"T_IDENTIFIER","context":"normal","value":"payload","line":11,"start":223,"end":230},{"type":"T_PLUS","context":"normal","value":"+","line":11,"start":231,"end":232},{"type":"T_NUMBER","context":"normal","value":"10","line":11,"start":233,"end":235},{"type":"T_SEMICOLON","context":"normal","value":";","line":11,"start":235,"end":236},{"type":"T_RCURLY","context":"normal","value":"}","line":12,"start":239,"end":240},{"type":"T_RCURLY","context":"normal","value":"}","line":13,"start":241,"end":242}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot get `payload.length` because property `length` is missing in `Number` [1]. [prop-missing]","context":" case 'A': return payload.length;","source":"-","start":{"line":10,"column":30,"offset":194},"end":{"line":10,"column":35,"offset":200}}],"operation":null}]}
Fixed example:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
type Action =
| {type: 'A', payload: string}
| {type: 'B', payload: number};
const fn = (action: Action) => {
switch (action.type) {
case 'A': return action.payload.length;
case 'B': return action.payload + 10;
}
}
|
{"value":"/* @flow */\n\ntype Action =\n | {type: 'A', payload: string}\n | {type: 'B', payload: number};\n\n// OK\nconst fn = (action: Action) => {\n switch (action.type) {\n case 'A': return action.payload.length;\n case 'B': return action.payload + 10;\n }\n}\n","tokens":[{"type":"Block","context":"comment","value":"/* @flow */","line":1,"start":0,"end":11},{"type":"T_TYPE","context":"normal","value":"type","line":3,"start":13,"end":17},{"type":"T_IDENTIFIER","context":"type","value":"Action","line":3,"start":18,"end":24},{"type":"T_ASSIGN","context":"type","value":"=","line":3,"start":25,"end":26},{"type":"T_BIT_OR","context":"type","value":"|","line":4,"start":29,"end":30},{"type":"T_LCURLY","context":"type","value":"{","line":4,"start":31,"end":32},{"type":"T_TYPE","context":"normal","value":"type","line":4,"start":32,"end":36},{"type":"T_COLON","context":"type","value":":","line":4,"start":36,"end":37},{"type":"T_STRING","context":"type","value":"'A'","line":4,"start":38,"end":41},{"type":"T_COMMA","context":"type","value":",","line":4,"start":41,"end":42},{"type":"T_IDENTIFIER","context":"normal","value":"payload","line":4,"start":43,"end":50},{"type":"T_COLON","context":"type","value":":","line":4,"start":50,"end":51},{"type":"T_STRING_TYPE","context":"type","value":"string","line":4,"start":52,"end":58},{"type":"T_RCURLY","context":"type","value":"}","line":4,"start":58,"end":59},{"type":"T_BIT_OR","context":"type","value":"|","line":5,"start":62,"end":63},{"type":"T_LCURLY","context":"type","value":"{","line":5,"start":64,"end":65},{"type":"T_TYPE","context":"normal","value":"type","line":5,"start":65,"end":69},{"type":"T_COLON","context":"type","value":":","line":5,"start":69,"end":70},{"type":"T_STRING","context":"type","value":"'B'","line":5,"start":71,"end":74},{"type":"T_COMMA","context":"type","value":",","line":5,"start":74,"end":75},{"type":"T_IDENTIFIER","context":"normal","value":"payload","line":5,"start":76,"end":83},{"type":"T_COLON","context":"type","value":":","line":5,"start":83,"end":84},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":5,"start":85,"end":91},{"type":"T_RCURLY","context":"type","value":"}","line":5,"start":91,"end":92},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":92,"end":93},{"type":"Line","context":"comment","value":"// OK","line":7,"start":95,"end":100},{"type":"T_CONST","context":"normal","value":"const","line":8,"start":101,"end":106},{"type":"T_IDENTIFIER","context":"normal","value":"fn","line":8,"start":107,"end":109},{"type":"T_ASSIGN","context":"normal","value":"=","line":8,"start":110,"end":111},{"type":"T_LPAREN","context":"normal","value":"(","line":8,"start":112,"end":113},{"type":"T_IDENTIFIER","context":"normal","value":"action","line":8,"start":113,"end":119},{"type":"T_COLON","context":"type","value":":","line":8,"start":119,"end":120},{"type":"T_IDENTIFIER","context":"type","value":"Action","line":8,"start":121,"end":127},{"type":"T_RPAREN","context":"normal","value":")","line":8,"start":127,"end":128},{"type":"T_ARROW","context":"normal","value":"=>","line":8,"start":129,"end":131},{"type":"T_LCURLY","context":"normal","value":"{","line":8,"start":132,"end":133},{"type":"T_SWITCH","context":"normal","value":"switch","line":9,"start":136,"end":142},{"type":"T_LPAREN","context":"normal","value":"(","line":9,"start":143,"end":144},{"type":"T_IDENTIFIER","context":"normal","value":"action","line":9,"start":144,"end":150},{"type":"T_PERIOD","context":"normal","value":".","line":9,"start":150,"end":151},{"type":"T_TYPE","context":"normal","value":"type","line":9,"start":151,"end":155},{"type":"T_RPAREN","context":"normal","value":")","line":9,"start":155,"end":156},{"type":"T_LCURLY","context":"normal","value":"{","line":9,"start":157,"end":158},{"type":"T_CASE","context":"normal","value":"case","line":10,"start":163,"end":167},{"type":"T_STRING","context":"normal","value":"'A'","line":10,"start":168,"end":171},{"type":"T_COLON","context":"normal","value":":","line":10,"start":171,"end":172},{"type":"T_RETURN","context":"normal","value":"return","line":10,"start":173,"end":179},{"type":"T_IDENTIFIER","context":"normal","value":"action","line":10,"start":180,"end":186},{"type":"T_PERIOD","context":"normal","value":".","line":10,"start":186,"end":187},{"type":"T_IDENTIFIER","context":"normal","value":"payload","line":10,"start":187,"end":194},{"type":"T_PERIOD","context":"normal","value":".","line":10,"start":194,"end":195},{"type":"T_IDENTIFIER","context":"normal","value":"length","line":10,"start":195,"end":201},{"type":"T_SEMICOLON","context":"normal","value":";","line":10,"start":201,"end":202},{"type":"T_CASE","context":"normal","value":"case","line":11,"start":207,"end":211},{"type":"T_STRING","context":"normal","value":"'B'","line":11,"start":212,"end":215},{"type":"T_COLON","context":"normal","value":":","line":11,"start":215,"end":216},{"type":"T_RETURN","context":"normal","value":"return","line":11,"start":217,"end":223},{"type":"T_IDENTIFIER","context":"normal","value":"action","line":11,"start":224,"end":230},{"type":"T_PERIOD","context":"normal","value":".","line":11,"start":230,"end":231},{"type":"T_IDENTIFIER","context":"normal","value":"payload","line":11,"start":231,"end":238},{"type":"T_PLUS","context":"normal","value":"+","line":11,"start":239,"end":240},{"type":"T_NUMBER","context":"normal","value":"10","line":11,"start":241,"end":243},{"type":"T_SEMICOLON","context":"normal","value":";","line":11,"start":243,"end":244},{"type":"T_RCURLY","context":"normal","value":"}","line":12,"start":247,"end":248},{"type":"T_RCURLY","context":"normal","value":"}","line":13,"start":249,"end":250}],"errors":[]}
(https://flow.org/try)
Second example:
I got a “Missing type annotation” error. Where does it come from?
Flow requires type annotations at module boundaries to make sure it can scale. To read more about that, check out our blog post about that.
The most common case you’ll encounter is when exporting a function or React component. Flow requires you to annotate inputs. For instance, in this example, flow will complain:
1
2
|
export const add = a</span> => a + 1;
</code></pre></td>
</tr>
</tbody>
</table>
</div>
Cannot build a typed interface for this module. You should annotate the exports of this module with types. Missing type annotation at array pattern: [signature-verification-failure]
Cannot build a typed interface for this module. You should annotate the exports of this module with types. Missing type annotation at function return: [signature-verification-failure]
{"value":"// @flow\nexport const add = a => a + 1;\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_EXPORT","context":"normal","value":"export","line":2,"start":9,"end":15},{"type":"T_CONST","context":"normal","value":"const","line":2,"start":16,"end":21},{"type":"T_IDENTIFIER","context":"normal","value":"add","line":2,"start":22,"end":25},{"type":"T_ASSIGN","context":"normal","value":"=","line":2,"start":26,"end":27},{"type":"T_IDENTIFIER","context":"normal","value":"a","line":2,"start":28,"end":29},{"type":"T_ARROW","context":"normal","value":"=>","line":2,"start":30,"end":32},{"type":"T_IDENTIFIER","context":"normal","value":"a","line":2,"start":33,"end":34},{"type":"T_PLUS","context":"normal","value":"+","line":2,"start":35,"end":36},{"type":"T_NUMBER","context":"normal","value":"1","line":2,"start":37,"end":38},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":38,"end":39}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot build a typed interface for this module. You should annotate the exports of this module with types. Missing type annotation at array pattern: [signature-verification-failure]","context":"export const add = a => a + 1;","source":"-","start":{"line":2,"column":20,"offset":28},"end":{"line":2,"column":20,"offset":29}}],"operation":null},{"id":"E2","messages":[{"id":"E2M1","description":"Cannot build a typed interface for this module. You should annotate the exports of this module with types. Missing type annotation at function return: [signature-verification-failure]","context":"export const add = a => a + 1;","source":"-","start":{"line":2,"column":21,"offset":29},"end":{"line":2,"column":20,"offset":29}}],"operation":null}]}
</div>
The fix here is to add types to the parameters of `add`.
Example ([https://flow.org/try](https://flow.org/try/#0PTAEAEDMBsHsHcBQBTAHgB1gJwC6gMawB2AzngIYAmloAvKABTkBcoRArgLYBGyWAlHQB8ocqADUoAIwBuIA)):
1
2
|
export const add = (a: number)</span> => a + 1;
</code></pre></td>
</tr>
</tbody>
</table>
</div>
Cannot build a typed interface for this module. You should annotate the exports of this module with types. Missing type annotation at function return: [signature-verification-failure]
{"value":"// @flow\nexport const add = (a: number) => a + 1;\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_EXPORT","context":"normal","value":"export","line":2,"start":9,"end":15},{"type":"T_CONST","context":"normal","value":"const","line":2,"start":16,"end":21},{"type":"T_IDENTIFIER","context":"normal","value":"add","line":2,"start":22,"end":25},{"type":"T_ASSIGN","context":"normal","value":"=","line":2,"start":26,"end":27},{"type":"T_LPAREN","context":"normal","value":"(","line":2,"start":28,"end":29},{"type":"T_IDENTIFIER","context":"normal","value":"a","line":2,"start":29,"end":30},{"type":"T_COLON","context":"type","value":":","line":2,"start":30,"end":31},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":2,"start":32,"end":38},{"type":"T_RPAREN","context":"normal","value":")","line":2,"start":38,"end":39},{"type":"T_ARROW","context":"normal","value":"=>","line":2,"start":40,"end":42},{"type":"T_IDENTIFIER","context":"normal","value":"a","line":2,"start":43,"end":44},{"type":"T_PLUS","context":"normal","value":"+","line":2,"start":45,"end":46},{"type":"T_NUMBER","context":"normal","value":"1","line":2,"start":47,"end":48},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":48,"end":49}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot build a typed interface for this module. You should annotate the exports of this module with types. Missing type annotation at function return: [signature-verification-failure]","context":"export const add = (a: number) => a + 1;","source":"-","start":{"line":2,"column":31,"offset":39},"end":{"line":2,"column":30,"offset":39}}],"operation":null}]}
</div>
To see how you can annotate exported React components, check out our docs on [HOCs](../react/hoc/#toc-exporting-wrapped-components).
There are other cases where this happens, and they might be harder to understand. You'll get an error like `Missing type annotation for U` For instance, you wrote this [code](https://flow.org/try/#0PTAEAEDMBsHsHcBQiDGsB2BnALqAhgE4F4CeoAvKANoDkeNANKDQEY0C6iApgB4AOsArjRZcAcy7ouBAJYoAgkVIV8SkgDoAtnj4AKPBQB8+AJRA):
1
2
3
|
const array = ['a', 'b']
export const genericArray = array.map(a => a)
|
Cannot build a typed interface for this module. You should annotate the exports of this module with types. Cannot determine the type of this call expression. Please provide an annotation, e.g., by adding a type cast around this expression. [signature-verification-failure]
{"value":"// @flow\nconst array = ['a', 'b']\nexport const genericArray = array.map(a => a)\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_CONST","context":"normal","value":"const","line":2,"start":9,"end":14},{"type":"T_IDENTIFIER","context":"normal","value":"array","line":2,"start":15,"end":20},{"type":"T_ASSIGN","context":"normal","value":"=","line":2,"start":21,"end":22},{"type":"T_LBRACKET","context":"normal","value":"[","line":2,"start":23,"end":24},{"type":"T_STRING","context":"normal","value":"'a'","line":2,"start":24,"end":27},{"type":"T_COMMA","context":"normal","value":",","line":2,"start":27,"end":28},{"type":"T_STRING","context":"normal","value":"'b'","line":2,"start":29,"end":32},{"type":"T_RBRACKET","context":"normal","value":"]","line":2,"start":32,"end":33},{"type":"T_EXPORT","context":"normal","value":"export","line":3,"start":34,"end":40},{"type":"T_CONST","context":"normal","value":"const","line":3,"start":41,"end":46},{"type":"T_IDENTIFIER","context":"normal","value":"genericArray","line":3,"start":47,"end":59},{"type":"T_ASSIGN","context":"normal","value":"=","line":3,"start":60,"end":61},{"type":"T_IDENTIFIER","context":"normal","value":"array","line":3,"start":62,"end":67},{"type":"T_PERIOD","context":"normal","value":".","line":3,"start":67,"end":68},{"type":"T_IDENTIFIER","context":"normal","value":"map","line":3,"start":68,"end":71},{"type":"T_LPAREN","context":"normal","value":"(","line":3,"start":71,"end":72},{"type":"T_IDENTIFIER","context":"normal","value":"a","line":3,"start":72,"end":73},{"type":"T_ARROW","context":"normal","value":"=>","line":3,"start":74,"end":76},{"type":"T_IDENTIFIER","context":"normal","value":"a","line":3,"start":77,"end":78},{"type":"T_RPAREN","context":"normal","value":")","line":3,"start":78,"end":79}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot build a typed interface for this module. You should annotate the exports of this module with types. Cannot determine the type of this call expression. Please provide an annotation, e.g., by adding a type cast around this expression. [signature-verification-failure]","context":"export const genericArray = array.map(a => a)","source":"-","start":{"line":3,"column":29,"offset":62},"end":{"line":3,"column":45,"offset":79}}],"operation":null}]}
Here, Flow will complain on the `export`, asking for a type annotation. Flow wants you to annotate exports returned by a generic function. The type of `Array.prototype.map` is `map(callbackfn: (value: T, index: number, array: Array) => U, thisArg?: any): Array`. The `` corresponds to what is called a [generic](https://flow.org/en/docs/types/generics/), to express the fact that the type of the function passed to map is linked to the type of the array.
Understanding the logic behind generics might be useful, but what you really need to know to make your typings valid is that you need to help Flow to understand the type of `genericArray`.
You can do that by adding an explicit type argument:
1
2
3
|
const array = ['a', 'b'];
export const genericArray = array.map<string>(a => a);
|
Cannot build a typed interface for this module. You should annotate the exports of this module with types. Cannot determine the type of this call expression. Please provide an annotation, e.g., by adding a type cast around this expression. [signature-verification-failure]
{"value":"// @flow\nconst array = ['a', 'b'];\nexport const genericArray = array.map<string>(a => a);\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_CONST","context":"normal","value":"const","line":2,"start":9,"end":14},{"type":"T_IDENTIFIER","context":"normal","value":"array","line":2,"start":15,"end":20},{"type":"T_ASSIGN","context":"normal","value":"=","line":2,"start":21,"end":22},{"type":"T_LBRACKET","context":"normal","value":"[","line":2,"start":23,"end":24},{"type":"T_STRING","context":"normal","value":"'a'","line":2,"start":24,"end":27},{"type":"T_COMMA","context":"normal","value":",","line":2,"start":27,"end":28},{"type":"T_STRING","context":"normal","value":"'b'","line":2,"start":29,"end":32},{"type":"T_RBRACKET","context":"normal","value":"]","line":2,"start":32,"end":33},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":33,"end":34},{"type":"T_EXPORT","context":"normal","value":"export","line":3,"start":35,"end":41},{"type":"T_CONST","context":"normal","value":"const","line":3,"start":42,"end":47},{"type":"T_IDENTIFIER","context":"normal","value":"genericArray","line":3,"start":48,"end":60},{"type":"T_ASSIGN","context":"normal","value":"=","line":3,"start":61,"end":62},{"type":"T_IDENTIFIER","context":"normal","value":"array","line":3,"start":63,"end":68},{"type":"T_PERIOD","context":"normal","value":".","line":3,"start":68,"end":69},{"type":"T_IDENTIFIER","context":"normal","value":"map","line":3,"start":69,"end":72},{"type":"T_LESS_THAN","context":"normal","value":"<","line":3,"start":72,"end":73},{"type":"T_STRING_TYPE","context":"type","value":"string","line":3,"start":73,"end":79},{"type":"T_GREATER_THAN","context":"normal","value":">","line":3,"start":79,"end":80},{"type":"T_LPAREN","context":"normal","value":"(","line":3,"start":80,"end":81},{"type":"T_IDENTIFIER","context":"normal","value":"a","line":3,"start":81,"end":82},{"type":"T_ARROW","context":"normal","value":"=>","line":3,"start":83,"end":85},{"type":"T_IDENTIFIER","context":"normal","value":"a","line":3,"start":86,"end":87},{"type":"T_RPAREN","context":"normal","value":")","line":3,"start":87,"end":88},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":88,"end":89}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot build a typed interface for this module. You should annotate the exports of this module with types. Cannot determine the type of this call expression. Please provide an annotation, e.g., by adding a type cast around this expression. [signature-verification-failure]","context":"export const genericArray = array.map<string>(a => a);","source":"-","start":{"line":3,"column":29,"offset":63},"end":{"line":3,"column":53,"offset":88}}],"operation":null}]}
or by annotating the exported constant ([https://flow.org/try](https://flow.org/try/#0PTAEAEDMBsHsHcBQBjWA7AzgF1AQwE764CeoAvKANoDku1ANKNQEbUC6iApgB4AOs+HKkw4A5pzSd8AS2QBBQiQBcoBUWIAebDLSiAfOTyLiAOgC2uXgApc5A7gCUQA)):
1
2
3
|
const array = ['a', 'b']
export const genericArray: Array<string> = array.map(a => a)
|
{"value":"// @flow\nconst array = ['a', 'b']\nexport const genericArray: Array<string> = array.map(a => a)\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_CONST","context":"normal","value":"const","line":2,"start":9,"end":14},{"type":"T_IDENTIFIER","context":"normal","value":"array","line":2,"start":15,"end":20},{"type":"T_ASSIGN","context":"normal","value":"=","line":2,"start":21,"end":22},{"type":"T_LBRACKET","context":"normal","value":"[","line":2,"start":23,"end":24},{"type":"T_STRING","context":"normal","value":"'a'","line":2,"start":24,"end":27},{"type":"T_COMMA","context":"normal","value":",","line":2,"start":27,"end":28},{"type":"T_STRING","context":"normal","value":"'b'","line":2,"start":29,"end":32},{"type":"T_RBRACKET","context":"normal","value":"]","line":2,"start":32,"end":33},{"type":"T_EXPORT","context":"normal","value":"export","line":3,"start":34,"end":40},{"type":"T_CONST","context":"normal","value":"const","line":3,"start":41,"end":46},{"type":"T_IDENTIFIER","context":"normal","value":"genericArray","line":3,"start":47,"end":59},{"type":"T_COLON","context":"type","value":":","line":3,"start":59,"end":60},{"type":"T_IDENTIFIER","context":"type","value":"Array","line":3,"start":61,"end":66},{"type":"T_LESS_THAN","context":"type","value":"<","line":3,"start":66,"end":67},{"type":"T_STRING_TYPE","context":"type","value":"string","line":3,"start":67,"end":73},{"type":"T_GREATER_THAN","context":"type","value":">","line":3,"start":73,"end":74},{"type":"T_ASSIGN","context":"normal","value":"=","line":3,"start":75,"end":76},{"type":"T_IDENTIFIER","context":"normal","value":"array","line":3,"start":77,"end":82},{"type":"T_PERIOD","context":"normal","value":".","line":3,"start":82,"end":83},{"type":"T_IDENTIFIER","context":"normal","value":"map","line":3,"start":83,"end":86},{"type":"T_LPAREN","context":"normal","value":"(","line":3,"start":86,"end":87},{"type":"T_IDENTIFIER","context":"normal","value":"a","line":3,"start":87,"end":88},{"type":"T_ARROW","context":"normal","value":"=>","line":3,"start":89,"end":91},{"type":"T_IDENTIFIER","context":"normal","value":"a","line":3,"start":92,"end":93},{"type":"T_RPAREN","context":"normal","value":")","line":3,"start":93,"end":94}],"errors":[]}
### Flow cannot understand the types of my higher order React component, how can I help it?
Typings HOCs can be complicated. While you can follow the [docs about it](https://flow.org/en/docs/react/hoc/), sometimes it can be easier to type the returned component.
For instance, in this [example](https://flow.org/try/#0PTAEAEDMBsHsHcBQBLAtgB1gJwC6gFSgCGAzqAEoCmRAxnpFrKqAORbV0sDciiNsAOxJ4SlHABUAnukqgAvKABCpSgGEmmAZQF45APlDpG6MvtAAeZaPUZB2vAG8AdC6OwTAX1A5plOQCIAIwBXHBxBf1BgPR5+ITwAcW1KLGQaRVDwgXlQAAoHHxkAGlAaAAtkaAATdgEPAEp5A3MQsMFvXzkC3y9BVWg0gGsu3MazOJJYaEonOABzXJYaAZpByiqWeo89B3LKmu0Pc2BWrJjeCbwMtoEALgoOHCcbTXspGXNdiura+6paJ4AOVgVUo2xyogkvlySS0qXSmUE9S4QA), we don't type the HOC (setType), but the component created with it, `Button`. To do so, we use the type `React.ComponentType`.
1
2
3
4
5
6
7
|
import * as React from 'react';
const setType = BaseComponent => props => <BaseComponent {...props} type="button" />;
const GenericButton = ({type, children}) => <button type={type} onClick={() => console.log('clicked')}>{children}</button>;
const Button: React.ComponentType<{children: React.Node}> = setType(GenericButton);
|
{"value":"// @flow\nimport * as React from 'react';\n\nconst setType = BaseComponent => props => <BaseComponent {...props} type=\"button\" />;\nconst GenericButton = ({type, children}) => <button type={type} onClick={() => console.log('clicked')}>{children}</button>;\n\nconst Button: React.ComponentType<{children: React.Node}> = setType(GenericButton);\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_IMPORT","context":"normal","value":"import","line":2,"start":9,"end":15},{"type":"T_MULT","context":"normal","value":"*","line":2,"start":16,"end":17},{"type":"T_IDENTIFIER","context":"normal","value":"as","line":2,"start":18,"end":20},{"type":"T_IDENTIFIER","context":"normal","value":"React","line":2,"start":21,"end":26},{"type":"T_IDENTIFIER","context":"normal","value":"from","line":2,"start":27,"end":31},{"type":"T_STRING","context":"normal","value":"'react'","line":2,"start":32,"end":39},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":39,"end":40},{"type":"T_CONST","context":"normal","value":"const","line":4,"start":42,"end":47},{"type":"T_IDENTIFIER","context":"normal","value":"setType","line":4,"start":48,"end":55},{"type":"T_ASSIGN","context":"normal","value":"=","line":4,"start":56,"end":57},{"type":"T_IDENTIFIER","context":"normal","value":"BaseComponent","line":4,"start":58,"end":71},{"type":"T_ARROW","context":"normal","value":"=>","line":4,"start":72,"end":74},{"type":"T_IDENTIFIER","context":"normal","value":"props","line":4,"start":75,"end":80},{"type":"T_ARROW","context":"normal","value":"=>","line":4,"start":81,"end":83},{"type":"T_LESS_THAN","context":"jsxTag","value":"<","line":4,"start":84,"end":85},{"type":"T_JSX_IDENTIFIER","context":"jsxTag","value":"BaseComponent","line":4,"start":85,"end":98},{"type":"T_LCURLY","context":"normal","value":"{","line":4,"start":99,"end":100},{"type":"T_ELLIPSIS","context":"normal","value":"...","line":4,"start":100,"end":103},{"type":"T_IDENTIFIER","context":"normal","value":"props","line":4,"start":103,"end":108},{"type":"T_RCURLY","context":"normal","value":"}","line":4,"start":108,"end":109},{"type":"T_JSX_IDENTIFIER","context":"jsxTag","value":"type","line":4,"start":110,"end":114},{"type":"T_ASSIGN","context":"jsxTag","value":"=","line":4,"start":114,"end":115},{"type":"T_JSX_TEXT","context":"jsxTag","value":"\"button\"","line":4,"start":115,"end":123},{"type":"T_DIV","context":"jsxTag","value":"/","line":4,"start":124,"end":125},{"type":"T_GREATER_THAN","context":"jsxTag","value":">","line":4,"start":125,"end":126},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":126,"end":127},{"type":"T_CONST","context":"normal","value":"const","line":5,"start":128,"end":133},{"type":"T_IDENTIFIER","context":"normal","value":"GenericButton","line":5,"start":134,"end":147},{"type":"T_ASSIGN","context":"normal","value":"=","line":5,"start":148,"end":149},{"type":"T_LPAREN","context":"normal","value":"(","line":5,"start":150,"end":151},{"type":"T_LCURLY","context":"normal","value":"{","line":5,"start":151,"end":152},{"type":"T_TYPE","context":"normal","value":"type","line":5,"start":152,"end":156},{"type":"T_COMMA","context":"normal","value":",","line":5,"start":156,"end":157},{"type":"T_IDENTIFIER","context":"normal","value":"children","line":5,"start":158,"end":166},{"type":"T_RCURLY","context":"normal","value":"}","line":5,"start":166,"end":167},{"type":"T_RPAREN","context":"normal","value":")","line":5,"start":167,"end":168},{"type":"T_ARROW","context":"normal","value":"=>","line":5,"start":169,"end":171},{"type":"T_LESS_THAN","context":"jsxTag","value":"<","line":5,"start":172,"end":173},{"type":"T_JSX_IDENTIFIER","context":"jsxTag","value":"button","line":5,"start":173,"end":179},{"type":"T_JSX_IDENTIFIER","context":"jsxTag","value":"type","line":5,"start":180,"end":184},{"type":"T_ASSIGN","context":"jsxTag","value":"=","line":5,"start":184,"end":185},{"type":"T_LCURLY","context":"normal","value":"{","line":5,"start":185,"end":186},{"type":"T_TYPE","context":"normal","value":"type","line":5,"start":186,"end":190},{"type":"T_RCURLY","context":"normal","value":"}","line":5,"start":190,"end":191},{"type":"T_JSX_IDENTIFIER","context":"jsxTag","value":"onClick","line":5,"start":192,"end":199},{"type":"T_ASSIGN","context":"jsxTag","value":"=","line":5,"start":199,"end":200},{"type":"T_LCURLY","context":"normal","value":"{","line":5,"start":200,"end":201},{"type":"T_LPAREN","context":"normal","value":"(","line":5,"start":201,"end":202},{"type":"T_RPAREN","context":"normal","value":")","line":5,"start":202,"end":203},{"type":"T_ARROW","context":"normal","value":"=>","line":5,"start":204,"end":206},{"type":"T_IDENTIFIER","context":"normal","value":"console","line":5,"start":207,"end":214},{"type":"T_PERIOD","context":"normal","value":".","line":5,"start":214,"end":215},{"type":"T_IDENTIFIER","context":"normal","value":"log","line":5,"start":215,"end":218},{"type":"T_LPAREN","context":"normal","value":"(","line":5,"start":218,"end":219},{"type":"T_STRING","context":"normal","value":"'clicked'","line":5,"start":219,"end":228},{"type":"T_RPAREN","context":"normal","value":")","line":5,"start":228,"end":229},{"type":"T_RCURLY","context":"normal","value":"}","line":5,"start":229,"end":230},{"type":"T_GREATER_THAN","context":"jsxTag","value":">","line":5,"start":230,"end":231},{"type":"T_LCURLY","context":"normal","value":"{","line":5,"start":231,"end":232},{"type":"T_IDENTIFIER","context":"normal","value":"children","line":5,"start":232,"end":240},{"type":"T_RCURLY","context":"normal","value":"}","line":5,"start":240,"end":241},{"type":"T_LESS_THAN","context":"jsxTag","value":"<","line":5,"start":241,"end":242},{"type":"T_DIV","context":"jsxTag","value":"/","line":5,"start":242,"end":243},{"type":"T_JSX_IDENTIFIER","context":"jsxTag","value":"button","line":5,"start":243,"end":249},{"type":"T_GREATER_THAN","context":"jsxTag","value":">","line":5,"start":249,"end":250},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":250,"end":251},{"type":"T_CONST","context":"normal","value":"const","line":7,"start":253,"end":258},{"type":"T_IDENTIFIER","context":"normal","value":"Button","line":7,"start":259,"end":265},{"type":"T_COLON","context":"type","value":":","line":7,"start":265,"end":266},{"type":"T_IDENTIFIER","context":"type","value":"React","line":7,"start":267,"end":272},{"type":"T_PERIOD","context":"type","value":".","line":7,"start":272,"end":273},{"type":"T_IDENTIFIER","context":"type","value":"ComponentType","line":7,"start":273,"end":286},{"type":"T_LESS_THAN","context":"type","value":"<","line":7,"start":286,"end":287},{"type":"T_LCURLY","context":"type","value":"{","line":7,"start":287,"end":288},{"type":"T_IDENTIFIER","context":"normal","value":"children","line":7,"start":288,"end":296},{"type":"T_COLON","context":"type","value":":","line":7,"start":296,"end":297},{"type":"T_IDENTIFIER","context":"type","value":"React","line":7,"start":298,"end":303},{"type":"T_PERIOD","context":"type","value":".","line":7,"start":303,"end":304},{"type":"T_IDENTIFIER","context":"type","value":"Node","line":7,"start":304,"end":308},{"type":"T_RCURLY","context":"type","value":"}","line":7,"start":308,"end":309},{"type":"T_GREATER_THAN","context":"type","value":">","line":7,"start":309,"end":310},{"type":"T_ASSIGN","context":"normal","value":"=","line":7,"start":311,"end":312},{"type":"T_IDENTIFIER","context":"normal","value":"setType","line":7,"start":313,"end":320},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":320,"end":321},{"type":"T_IDENTIFIER","context":"normal","value":"GenericButton","line":7,"start":321,"end":334},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":334,"end":335},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":335,"end":336}],"errors":[]}
|
|