Objects can be used in many different ways in JavaScript. There are a number of
different ways to type them in order to support all the different use cases.
In Flow, there are two different kinds of object types: exact object types and inexact object types.
In general, we recommend using exact object types whenever possible. Exact object types are more
precise and interact better with other type system features, like spreads.
Object type syntax
Object types try to match the syntax for objects in JavaScript as much as
possible. Using curly braces {}
and name-value pairs using a colon :
split
by commas ,
.
1
2
3
4
5
6
7
8
9
10
11
|
var obj1: { foo: boolean } = { foo: true };
var obj2: {
foo: number,
bar: boolean,
baz: string,
} = {
foo: 1,
bar: true,
baz: 'three',
};
|
{"value":"// @flow\nvar obj1: { foo: boolean } = { foo: true };\nvar obj2: {\n foo: number,\n bar: boolean,\n baz: string,\n} = {\n foo: 1,\n bar: true,\n baz: 'three',\n};\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_VAR","context":"normal","value":"var","line":2,"start":9,"end":12},{"type":"T_IDENTIFIER","context":"normal","value":"obj1","line":2,"start":13,"end":17},{"type":"T_COLON","context":"type","value":":","line":2,"start":17,"end":18},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":19,"end":20},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":2,"start":21,"end":24},{"type":"T_COLON","context":"type","value":":","line":2,"start":24,"end":25},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":2,"start":26,"end":33},{"type":"T_RCURLY","context":"type","value":"}","line":2,"start":34,"end":35},{"type":"T_ASSIGN","context":"normal","value":"=","line":2,"start":36,"end":37},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":38,"end":39},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":2,"start":40,"end":43},{"type":"T_COLON","context":"normal","value":":","line":2,"start":43,"end":44},{"type":"T_TRUE","context":"normal","value":"true","line":2,"start":45,"end":49},{"type":"T_RCURLY","context":"normal","value":"}","line":2,"start":50,"end":51},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":51,"end":52},{"type":"T_VAR","context":"normal","value":"var","line":3,"start":53,"end":56},{"type":"T_IDENTIFIER","context":"normal","value":"obj2","line":3,"start":57,"end":61},{"type":"T_COLON","context":"type","value":":","line":3,"start":61,"end":62},{"type":"T_LCURLY","context":"type","value":"{","line":3,"start":63,"end":64},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":4,"start":67,"end":70},{"type":"T_COLON","context":"type","value":":","line":4,"start":70,"end":71},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":4,"start":72,"end":78},{"type":"T_COMMA","context":"type","value":",","line":4,"start":78,"end":79},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":5,"start":82,"end":85},{"type":"T_COLON","context":"type","value":":","line":5,"start":85,"end":86},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":5,"start":87,"end":94},{"type":"T_COMMA","context":"type","value":",","line":5,"start":94,"end":95},{"type":"T_IDENTIFIER","context":"normal","value":"baz","line":6,"start":98,"end":101},{"type":"T_COLON","context":"type","value":":","line":6,"start":101,"end":102},{"type":"T_STRING_TYPE","context":"type","value":"string","line":6,"start":103,"end":109},{"type":"T_COMMA","context":"type","value":",","line":6,"start":109,"end":110},{"type":"T_RCURLY","context":"type","value":"}","line":7,"start":111,"end":112},{"type":"T_ASSIGN","context":"normal","value":"=","line":7,"start":113,"end":114},{"type":"T_LCURLY","context":"normal","value":"{","line":7,"start":115,"end":116},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":8,"start":119,"end":122},{"type":"T_COLON","context":"normal","value":":","line":8,"start":122,"end":123},{"type":"T_NUMBER","context":"normal","value":"1","line":8,"start":124,"end":125},{"type":"T_COMMA","context":"normal","value":",","line":8,"start":125,"end":126},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":9,"start":129,"end":132},{"type":"T_COLON","context":"normal","value":":","line":9,"start":132,"end":133},{"type":"T_TRUE","context":"normal","value":"true","line":9,"start":134,"end":138},{"type":"T_COMMA","context":"normal","value":",","line":9,"start":138,"end":139},{"type":"T_IDENTIFIER","context":"normal","value":"baz","line":10,"start":142,"end":145},{"type":"T_COLON","context":"normal","value":":","line":10,"start":145,"end":146},{"type":"T_STRING","context":"normal","value":"'three'","line":10,"start":147,"end":154},{"type":"T_COMMA","context":"normal","value":",","line":10,"start":154,"end":155},{"type":"T_RCURLY","context":"normal","value":"}","line":11,"start":156,"end":157},{"type":"T_SEMICOLON","context":"normal","value":";","line":11,"start":157,"end":158}],"errors":[]}
Note: Previously object types used semicolons ;
for splitting
name-value pairs. While the syntax is still valid, you should use commas ,
.
Optional object type properties
In JavaScript, accessing a property that doesn’t exist evaluates to
undefined
. This is a common source of errors in JavaScript programs, so Flow
turns these into type errors.
1
2
3
4
|
var obj = { foo: "bar" };
obj.bar;
|
Cannot get `obj.bar` because property `bar` is missing in object literal [1]. [prop-missing]
{"value":"// @flow\nvar obj = { foo: \"bar\" };\n// $ExpectError\nobj.bar; // Error!\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_VAR","context":"normal","value":"var","line":2,"start":9,"end":12},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":2,"start":13,"end":16},{"type":"T_ASSIGN","context":"normal","value":"=","line":2,"start":17,"end":18},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":19,"end":20},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":2,"start":21,"end":24},{"type":"T_COLON","context":"normal","value":":","line":2,"start":24,"end":25},{"type":"T_STRING","context":"normal","value":"\"bar\"","line":2,"start":26,"end":31},{"type":"T_RCURLY","context":"normal","value":"}","line":2,"start":32,"end":33},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":33,"end":34},{"type":"Line","context":"comment","value":"// $ExpectError","line":3,"start":35,"end":50},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":4,"start":51,"end":54},{"type":"T_PERIOD","context":"normal","value":".","line":4,"start":54,"end":55},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":4,"start":55,"end":58},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":58,"end":59},{"type":"Line","context":"comment","value":"// Error!","line":4,"start":60,"end":69}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot get `obj.bar` because property `bar` is missing in object literal [1]. [prop-missing]","context":"obj.bar; // Error!","source":"-","start":{"line":4,"column":5,"offset":55},"end":{"line":4,"column":7,"offset":58}}],"operation":null}]}
If you have an object that sometimes does not have a property you can make it
an optional property by adding a question mark ?
after the property name in
the object type.
1
2
3
4
5
6
|
var obj: { foo?: boolean } = {};
obj.foo = true;
obj.foo = 'hello';
|
Cannot assign `'hello'` to `obj.foo` because string [1] is incompatible with boolean [2]. [incompatible-type]
{"value":"// @flow\nvar obj: { foo?: boolean } = {};\n\nobj.foo = true; // Works!\n// $ExpectError\nobj.foo = 'hello'; // Error!\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_VAR","context":"normal","value":"var","line":2,"start":9,"end":12},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":2,"start":13,"end":16},{"type":"T_COLON","context":"type","value":":","line":2,"start":16,"end":17},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":18,"end":19},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":2,"start":20,"end":23},{"type":"T_PLING","context":"type","value":"?","line":2,"start":23,"end":24},{"type":"T_COLON","context":"type","value":":","line":2,"start":24,"end":25},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":2,"start":26,"end":33},{"type":"T_RCURLY","context":"type","value":"}","line":2,"start":34,"end":35},{"type":"T_ASSIGN","context":"normal","value":"=","line":2,"start":36,"end":37},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":38,"end":39},{"type":"T_RCURLY","context":"normal","value":"}","line":2,"start":39,"end":40},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":40,"end":41},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":4,"start":43,"end":46},{"type":"T_PERIOD","context":"normal","value":".","line":4,"start":46,"end":47},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":4,"start":47,"end":50},{"type":"T_ASSIGN","context":"normal","value":"=","line":4,"start":51,"end":52},{"type":"T_TRUE","context":"normal","value":"true","line":4,"start":53,"end":57},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":57,"end":58},{"type":"Line","context":"comment","value":"// Works!","line":4,"start":62,"end":71},{"type":"Line","context":"comment","value":"// $ExpectError","line":5,"start":72,"end":87},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":6,"start":88,"end":91},{"type":"T_PERIOD","context":"normal","value":".","line":6,"start":91,"end":92},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":6,"start":92,"end":95},{"type":"T_ASSIGN","context":"normal","value":"=","line":6,"start":96,"end":97},{"type":"T_STRING","context":"normal","value":"'hello'","line":6,"start":98,"end":105},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":105,"end":106},{"type":"Line","context":"comment","value":"// Error!","line":6,"start":107,"end":116}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot assign `'hello'` to `obj.foo` because string [1] is incompatible with boolean [2]. [incompatible-type]","context":"obj.foo = 'hello'; // Error!","source":"-","start":{"line":6,"column":11,"offset":98},"end":{"line":6,"column":17,"offset":105}}],"operation":null}]}
In addition to their set value type, these optional properties can either be
void
or omitted altogether. However, they cannot be null
.
1
2
3
4
5
6
7
8
9
10
|
function acceptsObject(value: { foo?: string }) {
}
acceptsObject({ foo: "bar" });
acceptsObject({ foo: undefined });
acceptsObject({ foo: null });
acceptsObject({});
|
Cannot call `acceptsObject` with object literal bound to `value` because null [1] is incompatible with string [2] in property `foo`. [incompatible-call]
{"value":"// @flow\nfunction acceptsObject(value: { foo?: string }) {\n // ...\n}\n\nacceptsObject({ foo: \"bar\" }); // Works!\nacceptsObject({ foo: undefined }); // Works!\n// $ExpectError\nacceptsObject({ foo: null }); // Error!\nacceptsObject({}); // Works!\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_FUNCTION","context":"normal","value":"function","line":2,"start":9,"end":17},{"type":"T_IDENTIFIER","context":"normal","value":"acceptsObject","line":2,"start":18,"end":31},{"type":"T_LPAREN","context":"normal","value":"(","line":2,"start":31,"end":32},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":2,"start":32,"end":37},{"type":"T_COLON","context":"type","value":":","line":2,"start":37,"end":38},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":39,"end":40},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":2,"start":41,"end":44},{"type":"T_PLING","context":"type","value":"?","line":2,"start":44,"end":45},{"type":"T_COLON","context":"type","value":":","line":2,"start":45,"end":46},{"type":"T_STRING_TYPE","context":"type","value":"string","line":2,"start":47,"end":53},{"type":"T_RCURLY","context":"type","value":"}","line":2,"start":54,"end":55},{"type":"T_RPAREN","context":"normal","value":")","line":2,"start":55,"end":56},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":57,"end":58},{"type":"Line","context":"comment","value":"// ...","line":3,"start":61,"end":67},{"type":"T_RCURLY","context":"normal","value":"}","line":4,"start":68,"end":69},{"type":"T_IDENTIFIER","context":"normal","value":"acceptsObject","line":6,"start":71,"end":84},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":84,"end":85},{"type":"T_LCURLY","context":"normal","value":"{","line":6,"start":85,"end":86},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":6,"start":87,"end":90},{"type":"T_COLON","context":"normal","value":":","line":6,"start":90,"end":91},{"type":"T_STRING","context":"normal","value":"\"bar\"","line":6,"start":92,"end":97},{"type":"T_RCURLY","context":"normal","value":"}","line":6,"start":98,"end":99},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":99,"end":100},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":100,"end":101},{"type":"Line","context":"comment","value":"// Works!","line":6,"start":106,"end":115},{"type":"T_IDENTIFIER","context":"normal","value":"acceptsObject","line":7,"start":116,"end":129},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":129,"end":130},{"type":"T_LCURLY","context":"normal","value":"{","line":7,"start":130,"end":131},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":7,"start":132,"end":135},{"type":"T_COLON","context":"normal","value":":","line":7,"start":135,"end":136},{"type":"T_IDENTIFIER","context":"normal","value":"undefined","line":7,"start":137,"end":146},{"type":"T_RCURLY","context":"normal","value":"}","line":7,"start":147,"end":148},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":148,"end":149},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":149,"end":150},{"type":"Line","context":"comment","value":"// Works!","line":7,"start":151,"end":160},{"type":"Line","context":"comment","value":"// $ExpectError","line":8,"start":161,"end":176},{"type":"T_IDENTIFIER","context":"normal","value":"acceptsObject","line":9,"start":177,"end":190},{"type":"T_LPAREN","context":"normal","value":"(","line":9,"start":190,"end":191},{"type":"T_LCURLY","context":"normal","value":"{","line":9,"start":191,"end":192},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":9,"start":193,"end":196},{"type":"T_COLON","context":"normal","value":":","line":9,"start":196,"end":197},{"type":"T_NULL","context":"normal","value":"null","line":9,"start":198,"end":202},{"type":"T_RCURLY","context":"normal","value":"}","line":9,"start":203,"end":204},{"type":"T_RPAREN","context":"normal","value":")","line":9,"start":204,"end":205},{"type":"T_SEMICOLON","context":"normal","value":";","line":9,"start":205,"end":206},{"type":"Line","context":"comment","value":"// Error!","line":9,"start":212,"end":221},{"type":"T_IDENTIFIER","context":"normal","value":"acceptsObject","line":10,"start":222,"end":235},{"type":"T_LPAREN","context":"normal","value":"(","line":10,"start":235,"end":236},{"type":"T_LCURLY","context":"normal","value":"{","line":10,"start":236,"end":237},{"type":"T_RCURLY","context":"normal","value":"}","line":10,"start":237,"end":238},{"type":"T_RPAREN","context":"normal","value":")","line":10,"start":238,"end":239},{"type":"T_SEMICOLON","context":"normal","value":";","line":10,"start":239,"end":240},{"type":"Line","context":"comment","value":"// Works!","line":10,"start":257,"end":266}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot call `acceptsObject` with object literal bound to `value` because null [1] is incompatible with string [2] in property `foo`. [incompatible-call]","context":"acceptsObject({ foo: null }); // Error!","source":"-","start":{"line":9,"column":22,"offset":198},"end":{"line":9,"column":25,"offset":202}}],"operation":null}]}
Object type inference
Flow can infer the type of object literals in two different ways depending on
how they are used.
Sealed objects
When you create an object with its properties, you create a sealed object
type in Flow. These sealed objects will know all of the properties you declared
them with and the types of their values.
1
2
3
4
5
6
7
8
9
10
11
12
|
var obj = {
foo: 1,
bar: true,
baz: 'three'
};
var foo: number = obj.foo;
var bar: boolean = obj.bar;
var baz: null = obj.baz;
var bat: string = obj.bat;
|
Cannot assign `obj.baz` to `baz` because string [1] is incompatible with null [2]. [incompatible-type]
Cannot get `obj.bat` because property `bat` (did you mean `bar`?) is missing in object literal [1]. [prop-missing]
{"value":"// @flow\nvar obj = {\n foo: 1,\n bar: true,\n baz: 'three'\n};\n\nvar foo: number = obj.foo; // Works!\nvar bar: boolean = obj.bar; // Works!\n// $ExpectError\nvar baz: null = obj.baz; // Error!\nvar bat: string = obj.bat; // Error!\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_VAR","context":"normal","value":"var","line":2,"start":9,"end":12},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":2,"start":13,"end":16},{"type":"T_ASSIGN","context":"normal","value":"=","line":2,"start":17,"end":18},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":19,"end":20},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":3,"start":23,"end":26},{"type":"T_COLON","context":"normal","value":":","line":3,"start":26,"end":27},{"type":"T_NUMBER","context":"normal","value":"1","line":3,"start":28,"end":29},{"type":"T_COMMA","context":"normal","value":",","line":3,"start":29,"end":30},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":4,"start":33,"end":36},{"type":"T_COLON","context":"normal","value":":","line":4,"start":36,"end":37},{"type":"T_TRUE","context":"normal","value":"true","line":4,"start":38,"end":42},{"type":"T_COMMA","context":"normal","value":",","line":4,"start":42,"end":43},{"type":"T_IDENTIFIER","context":"normal","value":"baz","line":5,"start":46,"end":49},{"type":"T_COLON","context":"normal","value":":","line":5,"start":49,"end":50},{"type":"T_STRING","context":"normal","value":"'three'","line":5,"start":51,"end":58},{"type":"T_RCURLY","context":"normal","value":"}","line":6,"start":59,"end":60},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":60,"end":61},{"type":"T_VAR","context":"normal","value":"var","line":8,"start":63,"end":66},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":8,"start":67,"end":70},{"type":"T_COLON","context":"type","value":":","line":8,"start":70,"end":71},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":8,"start":72,"end":78},{"type":"T_ASSIGN","context":"normal","value":"=","line":8,"start":80,"end":81},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":8,"start":82,"end":85},{"type":"T_PERIOD","context":"normal","value":".","line":8,"start":85,"end":86},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":8,"start":86,"end":89},{"type":"T_SEMICOLON","context":"normal","value":";","line":8,"start":89,"end":90},{"type":"Line","context":"comment","value":"// Works!","line":8,"start":91,"end":100},{"type":"T_VAR","context":"normal","value":"var","line":9,"start":101,"end":104},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":9,"start":105,"end":108},{"type":"T_COLON","context":"type","value":":","line":9,"start":108,"end":109},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":9,"start":110,"end":117},{"type":"T_ASSIGN","context":"normal","value":"=","line":9,"start":118,"end":119},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":9,"start":120,"end":123},{"type":"T_PERIOD","context":"normal","value":".","line":9,"start":123,"end":124},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":9,"start":124,"end":127},{"type":"T_SEMICOLON","context":"normal","value":";","line":9,"start":127,"end":128},{"type":"Line","context":"comment","value":"// Works!","line":9,"start":129,"end":138},{"type":"Line","context":"comment","value":"// $ExpectError","line":10,"start":139,"end":154},{"type":"T_VAR","context":"normal","value":"var","line":11,"start":155,"end":158},{"type":"T_IDENTIFIER","context":"normal","value":"baz","line":11,"start":159,"end":162},{"type":"T_COLON","context":"type","value":":","line":11,"start":162,"end":163},{"type":"T_NULL","context":"type","value":"null","line":11,"start":164,"end":168},{"type":"T_ASSIGN","context":"normal","value":"=","line":11,"start":172,"end":173},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":11,"start":174,"end":177},{"type":"T_PERIOD","context":"normal","value":".","line":11,"start":177,"end":178},{"type":"T_IDENTIFIER","context":"normal","value":"baz","line":11,"start":178,"end":181},{"type":"T_SEMICOLON","context":"normal","value":";","line":11,"start":181,"end":182},{"type":"Line","context":"comment","value":"// Error!","line":11,"start":183,"end":192},{"type":"T_VAR","context":"normal","value":"var","line":12,"start":193,"end":196},{"type":"T_IDENTIFIER","context":"normal","value":"bat","line":12,"start":197,"end":200},{"type":"T_COLON","context":"type","value":":","line":12,"start":200,"end":201},{"type":"T_STRING_TYPE","context":"type","value":"string","line":12,"start":202,"end":208},{"type":"T_ASSIGN","context":"normal","value":"=","line":12,"start":210,"end":211},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":12,"start":212,"end":215},{"type":"T_PERIOD","context":"normal","value":".","line":12,"start":215,"end":216},{"type":"T_IDENTIFIER","context":"normal","value":"bat","line":12,"start":216,"end":219},{"type":"T_SEMICOLON","context":"normal","value":";","line":12,"start":219,"end":220},{"type":"Line","context":"comment","value":"// Error!","line":12,"start":221,"end":230}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot assign `obj.baz` to `baz` because string [1] is incompatible with null [2]. [incompatible-type]","context":"var baz: null = obj.baz; // Error!","source":"-","start":{"line":11,"column":20,"offset":174},"end":{"line":11,"column":26,"offset":181}}],"operation":null},{"id":"E2","messages":[{"id":"E2M1","description":"Cannot get `obj.bat` because property `bat` (did you mean `bar`?) is missing in object literal [1]. [prop-missing]","context":"var bat: string = obj.bat; // Error!","source":"-","start":{"line":12,"column":24,"offset":216},"end":{"line":12,"column":26,"offset":219}}],"operation":null}]}
But when objects are sealed, Flow will not allow you to add new properties to
them.
1
2
3
4
5
6
7
8
9
|
var obj = {
foo: 1
};
obj.bar = true;
obj.baz = 'three';
|
Cannot assign `true` to `obj.bar` because property `bar` is missing in object literal [1]. [prop-missing]
Cannot assign `'three'` to `obj.baz` because property `baz` is missing in object literal [1]. [prop-missing]
{"value":"// @flow\nvar obj = {\n foo: 1\n};\n\n// $ExpectError\nobj.bar = true; // Error!\n// $ExpectError\nobj.baz = 'three'; // Error!\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_VAR","context":"normal","value":"var","line":2,"start":9,"end":12},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":2,"start":13,"end":16},{"type":"T_ASSIGN","context":"normal","value":"=","line":2,"start":17,"end":18},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":19,"end":20},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":3,"start":23,"end":26},{"type":"T_COLON","context":"normal","value":":","line":3,"start":26,"end":27},{"type":"T_NUMBER","context":"normal","value":"1","line":3,"start":28,"end":29},{"type":"T_RCURLY","context":"normal","value":"}","line":4,"start":30,"end":31},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":31,"end":32},{"type":"Line","context":"comment","value":"// $ExpectError","line":6,"start":34,"end":49},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":7,"start":50,"end":53},{"type":"T_PERIOD","context":"normal","value":".","line":7,"start":53,"end":54},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":7,"start":54,"end":57},{"type":"T_ASSIGN","context":"normal","value":"=","line":7,"start":58,"end":59},{"type":"T_TRUE","context":"normal","value":"true","line":7,"start":60,"end":64},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":64,"end":65},{"type":"Line","context":"comment","value":"// Error!","line":7,"start":69,"end":78},{"type":"Line","context":"comment","value":"// $ExpectError","line":8,"start":79,"end":94},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":9,"start":95,"end":98},{"type":"T_PERIOD","context":"normal","value":".","line":9,"start":98,"end":99},{"type":"T_IDENTIFIER","context":"normal","value":"baz","line":9,"start":99,"end":102},{"type":"T_ASSIGN","context":"normal","value":"=","line":9,"start":103,"end":104},{"type":"T_STRING","context":"normal","value":"'three'","line":9,"start":105,"end":112},{"type":"T_SEMICOLON","context":"normal","value":";","line":9,"start":112,"end":113},{"type":"Line","context":"comment","value":"// Error!","line":9,"start":114,"end":123}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot assign `true` to `obj.bar` because property `bar` is missing in object literal [1]. [prop-missing]","context":"obj.bar = true; // Error!","source":"-","start":{"line":7,"column":5,"offset":54},"end":{"line":7,"column":7,"offset":57}}],"operation":null},{"id":"E2","messages":[{"id":"E2M1","description":"Cannot assign `'three'` to `obj.baz` because property `baz` is missing in object literal [1]. [prop-missing]","context":"obj.baz = 'three'; // Error!","source":"-","start":{"line":9,"column":5,"offset":99},"end":{"line":9,"column":7,"offset":102}}],"operation":null}]}
The workaround here might be to turn your object into an unsealed object.
Unsealed objects
When you create an object without any properties, you create an unsealed
object type in Flow. These unsealed objects will not know all of their
properties and will allow you to add new ones.
1
2
3
4
5
6
|
var obj = {};
obj.foo = 1;
obj.bar = true;
obj.baz = 'three';
|
{"value":"// @flow\nvar obj = {};\n\nobj.foo = 1; // Works!\nobj.bar = true; // Works!\nobj.baz = 'three'; // Works!\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_VAR","context":"normal","value":"var","line":2,"start":9,"end":12},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":2,"start":13,"end":16},{"type":"T_ASSIGN","context":"normal","value":"=","line":2,"start":17,"end":18},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":19,"end":20},{"type":"T_RCURLY","context":"normal","value":"}","line":2,"start":20,"end":21},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":21,"end":22},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":4,"start":24,"end":27},{"type":"T_PERIOD","context":"normal","value":".","line":4,"start":27,"end":28},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":4,"start":28,"end":31},{"type":"T_ASSIGN","context":"normal","value":"=","line":4,"start":32,"end":33},{"type":"T_NUMBER","context":"normal","value":"1","line":4,"start":34,"end":35},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":35,"end":36},{"type":"Line","context":"comment","value":"// Works!","line":4,"start":43,"end":52},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":5,"start":53,"end":56},{"type":"T_PERIOD","context":"normal","value":".","line":5,"start":56,"end":57},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":5,"start":57,"end":60},{"type":"T_ASSIGN","context":"normal","value":"=","line":5,"start":61,"end":62},{"type":"T_TRUE","context":"normal","value":"true","line":5,"start":63,"end":67},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":67,"end":68},{"type":"Line","context":"comment","value":"// Works!","line":5,"start":72,"end":81},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":6,"start":82,"end":85},{"type":"T_PERIOD","context":"normal","value":".","line":6,"start":85,"end":86},{"type":"T_IDENTIFIER","context":"normal","value":"baz","line":6,"start":86,"end":89},{"type":"T_ASSIGN","context":"normal","value":"=","line":6,"start":90,"end":91},{"type":"T_STRING","context":"normal","value":"'three'","line":6,"start":92,"end":99},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":99,"end":100},{"type":"Line","context":"comment","value":"// Works!","line":6,"start":101,"end":110}],"errors":[]}
The inferred type of the property becomes what you set it to.
1
2
3
4
|
var obj = {};
obj.foo = 42;
var num: number = obj.foo;
|
{"value":"// @flow\nvar obj = {};\nobj.foo = 42;\nvar num: number = obj.foo;\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_VAR","context":"normal","value":"var","line":2,"start":9,"end":12},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":2,"start":13,"end":16},{"type":"T_ASSIGN","context":"normal","value":"=","line":2,"start":17,"end":18},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":19,"end":20},{"type":"T_RCURLY","context":"normal","value":"}","line":2,"start":20,"end":21},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":21,"end":22},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":3,"start":23,"end":26},{"type":"T_PERIOD","context":"normal","value":".","line":3,"start":26,"end":27},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":3,"start":27,"end":30},{"type":"T_ASSIGN","context":"normal","value":"=","line":3,"start":31,"end":32},{"type":"T_NUMBER","context":"normal","value":"42","line":3,"start":33,"end":35},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":35,"end":36},{"type":"T_VAR","context":"normal","value":"var","line":4,"start":37,"end":40},{"type":"T_IDENTIFIER","context":"normal","value":"num","line":4,"start":41,"end":44},{"type":"T_COLON","context":"type","value":":","line":4,"start":44,"end":45},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":4,"start":46,"end":52},{"type":"T_ASSIGN","context":"normal","value":"=","line":4,"start":53,"end":54},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":4,"start":55,"end":58},{"type":"T_PERIOD","context":"normal","value":".","line":4,"start":58,"end":59},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":4,"start":59,"end":62},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":62,"end":63}],"errors":[]}
Reassigning unsealed object properties
Similar to var
and let
variables
if you reassign a property of an unsealed object, by default Flow will give it
the type of all possible assignments.
1
2
3
4
5
6
7
8
9
10
11
|
var obj = {};
if (Math.random()) obj.prop = true;
else obj.prop = "hello";
var val1: boolean = obj.prop;
var val2: string = obj.prop;
var val3: boolean | string = obj.prop;
|
Cannot assign `obj.prop` to `val1` because string [1] is incompatible with boolean [2]. [incompatible-type]
Cannot assign `obj.prop` to `val2` because boolean [1] is incompatible with string [2]. [incompatible-type]
{"value":"// @flow\nvar obj = {};\n\nif (Math.random()) obj.prop = true;\nelse obj.prop = \"hello\";\n\n// $ExpectError\nvar val1: boolean = obj.prop; // Error!\n// $ExpectError\nvar val2: string = obj.prop; // Error!\nvar val3: boolean | string = obj.prop; // Works!\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_VAR","context":"normal","value":"var","line":2,"start":9,"end":12},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":2,"start":13,"end":16},{"type":"T_ASSIGN","context":"normal","value":"=","line":2,"start":17,"end":18},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":19,"end":20},{"type":"T_RCURLY","context":"normal","value":"}","line":2,"start":20,"end":21},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":21,"end":22},{"type":"T_IF","context":"normal","value":"if","line":4,"start":24,"end":26},{"type":"T_LPAREN","context":"normal","value":"(","line":4,"start":27,"end":28},{"type":"T_IDENTIFIER","context":"normal","value":"Math","line":4,"start":28,"end":32},{"type":"T_PERIOD","context":"normal","value":".","line":4,"start":32,"end":33},{"type":"T_IDENTIFIER","context":"normal","value":"random","line":4,"start":33,"end":39},{"type":"T_LPAREN","context":"normal","value":"(","line":4,"start":39,"end":40},{"type":"T_RPAREN","context":"normal","value":")","line":4,"start":40,"end":41},{"type":"T_RPAREN","context":"normal","value":")","line":4,"start":41,"end":42},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":4,"start":43,"end":46},{"type":"T_PERIOD","context":"normal","value":".","line":4,"start":46,"end":47},{"type":"T_IDENTIFIER","context":"normal","value":"prop","line":4,"start":47,"end":51},{"type":"T_ASSIGN","context":"normal","value":"=","line":4,"start":52,"end":53},{"type":"T_TRUE","context":"normal","value":"true","line":4,"start":54,"end":58},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":58,"end":59},{"type":"T_ELSE","context":"normal","value":"else","line":5,"start":60,"end":64},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":5,"start":65,"end":68},{"type":"T_PERIOD","context":"normal","value":".","line":5,"start":68,"end":69},{"type":"T_IDENTIFIER","context":"normal","value":"prop","line":5,"start":69,"end":73},{"type":"T_ASSIGN","context":"normal","value":"=","line":5,"start":74,"end":75},{"type":"T_STRING","context":"normal","value":"\"hello\"","line":5,"start":76,"end":83},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":83,"end":84},{"type":"Line","context":"comment","value":"// $ExpectError","line":7,"start":86,"end":101},{"type":"T_VAR","context":"normal","value":"var","line":8,"start":102,"end":105},{"type":"T_IDENTIFIER","context":"normal","value":"val1","line":8,"start":106,"end":110},{"type":"T_COLON","context":"type","value":":","line":8,"start":110,"end":111},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":8,"start":112,"end":119},{"type":"T_ASSIGN","context":"normal","value":"=","line":8,"start":120,"end":121},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":8,"start":122,"end":125},{"type":"T_PERIOD","context":"normal","value":".","line":8,"start":125,"end":126},{"type":"T_IDENTIFIER","context":"normal","value":"prop","line":8,"start":126,"end":130},{"type":"T_SEMICOLON","context":"normal","value":";","line":8,"start":130,"end":131},{"type":"Line","context":"comment","value":"// Error!","line":8,"start":132,"end":141},{"type":"Line","context":"comment","value":"// $ExpectError","line":9,"start":142,"end":157},{"type":"T_VAR","context":"normal","value":"var","line":10,"start":158,"end":161},{"type":"T_IDENTIFIER","context":"normal","value":"val2","line":10,"start":162,"end":166},{"type":"T_COLON","context":"type","value":":","line":10,"start":166,"end":167},{"type":"T_STRING_TYPE","context":"type","value":"string","line":10,"start":168,"end":174},{"type":"T_ASSIGN","context":"normal","value":"=","line":10,"start":176,"end":177},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":10,"start":178,"end":181},{"type":"T_PERIOD","context":"normal","value":".","line":10,"start":181,"end":182},{"type":"T_IDENTIFIER","context":"normal","value":"prop","line":10,"start":182,"end":186},{"type":"T_SEMICOLON","context":"normal","value":";","line":10,"start":186,"end":187},{"type":"Line","context":"comment","value":"// Error!","line":10,"start":188,"end":197},{"type":"T_VAR","context":"normal","value":"var","line":11,"start":198,"end":201},{"type":"T_IDENTIFIER","context":"normal","value":"val3","line":11,"start":202,"end":206},{"type":"T_COLON","context":"type","value":":","line":11,"start":206,"end":207},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":11,"start":208,"end":215},{"type":"T_BIT_OR","context":"type","value":"|","line":11,"start":216,"end":217},{"type":"T_STRING_TYPE","context":"type","value":"string","line":11,"start":218,"end":224},{"type":"T_ASSIGN","context":"normal","value":"=","line":11,"start":225,"end":226},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":11,"start":227,"end":230},{"type":"T_PERIOD","context":"normal","value":".","line":11,"start":230,"end":231},{"type":"T_IDENTIFIER","context":"normal","value":"prop","line":11,"start":231,"end":235},{"type":"T_SEMICOLON","context":"normal","value":";","line":11,"start":235,"end":236},{"type":"Line","context":"comment","value":"// Works!","line":11,"start":237,"end":246}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot assign `obj.prop` to `val1` because string [1] is incompatible with boolean [2]. [incompatible-type]","context":"var val1: boolean = obj.prop; // Error!","source":"-","start":{"line":8,"column":21,"offset":122},"end":{"line":8,"column":28,"offset":130}}],"operation":null},{"id":"E2","messages":[{"id":"E2M1","description":"Cannot assign `obj.prop` to `val2` because boolean [1] is incompatible with string [2]. [incompatible-type]","context":"var val2: string = obj.prop; // Error!","source":"-","start":{"line":10,"column":21,"offset":178},"end":{"line":10,"column":28,"offset":186}}],"operation":null}]}
Sometimes Flow is able to figure out (with certainty) the type of a property
after reassignment. In that case, Flow will give it the known type.
1
2
3
4
5
6
7
8
9
|
var obj = {};
obj.prop = true;
obj.prop = "hello";
var val1: boolean = obj.prop;
var val2: string = obj.prop;
|
Cannot assign `obj.prop` to `val1` because string [1] is incompatible with boolean [2]. [incompatible-type]
{"value":"// @flow\nvar obj = {};\n\nobj.prop = true;\nobj.prop = \"hello\";\n\n// $ExpectError\nvar val1: boolean = obj.prop; // Error!\nvar val2: string = obj.prop; // Works!\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_VAR","context":"normal","value":"var","line":2,"start":9,"end":12},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":2,"start":13,"end":16},{"type":"T_ASSIGN","context":"normal","value":"=","line":2,"start":17,"end":18},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":19,"end":20},{"type":"T_RCURLY","context":"normal","value":"}","line":2,"start":20,"end":21},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":21,"end":22},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":4,"start":24,"end":27},{"type":"T_PERIOD","context":"normal","value":".","line":4,"start":27,"end":28},{"type":"T_IDENTIFIER","context":"normal","value":"prop","line":4,"start":28,"end":32},{"type":"T_ASSIGN","context":"normal","value":"=","line":4,"start":33,"end":34},{"type":"T_TRUE","context":"normal","value":"true","line":4,"start":35,"end":39},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":39,"end":40},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":5,"start":41,"end":44},{"type":"T_PERIOD","context":"normal","value":".","line":5,"start":44,"end":45},{"type":"T_IDENTIFIER","context":"normal","value":"prop","line":5,"start":45,"end":49},{"type":"T_ASSIGN","context":"normal","value":"=","line":5,"start":50,"end":51},{"type":"T_STRING","context":"normal","value":"\"hello\"","line":5,"start":52,"end":59},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":59,"end":60},{"type":"Line","context":"comment","value":"// $ExpectError","line":7,"start":62,"end":77},{"type":"T_VAR","context":"normal","value":"var","line":8,"start":78,"end":81},{"type":"T_IDENTIFIER","context":"normal","value":"val1","line":8,"start":82,"end":86},{"type":"T_COLON","context":"type","value":":","line":8,"start":86,"end":87},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":8,"start":88,"end":95},{"type":"T_ASSIGN","context":"normal","value":"=","line":8,"start":96,"end":97},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":8,"start":98,"end":101},{"type":"T_PERIOD","context":"normal","value":".","line":8,"start":101,"end":102},{"type":"T_IDENTIFIER","context":"normal","value":"prop","line":8,"start":102,"end":106},{"type":"T_SEMICOLON","context":"normal","value":";","line":8,"start":106,"end":107},{"type":"Line","context":"comment","value":"// Error!","line":8,"start":108,"end":117},{"type":"T_VAR","context":"normal","value":"var","line":9,"start":118,"end":121},{"type":"T_IDENTIFIER","context":"normal","value":"val2","line":9,"start":122,"end":126},{"type":"T_COLON","context":"type","value":":","line":9,"start":126,"end":127},{"type":"T_STRING_TYPE","context":"type","value":"string","line":9,"start":128,"end":134},{"type":"T_ASSIGN","context":"normal","value":"=","line":9,"start":136,"end":137},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":9,"start":138,"end":141},{"type":"T_PERIOD","context":"normal","value":".","line":9,"start":141,"end":142},{"type":"T_IDENTIFIER","context":"normal","value":"prop","line":9,"start":142,"end":146},{"type":"T_SEMICOLON","context":"normal","value":";","line":9,"start":146,"end":147},{"type":"Line","context":"comment","value":"// Works!","line":9,"start":148,"end":157}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot assign `obj.prop` to `val1` because string [1] is incompatible with boolean [2]. [incompatible-type]","context":"var val1: boolean = obj.prop; // Error!","source":"-","start":{"line":8,"column":21,"offset":98},"end":{"line":8,"column":28,"offset":106}}],"operation":null}]}
As Flow gets smarter and smarter, it will figure out the types of properties in more scenarios.
Unknown property lookup on unsealed objects is unsafe
Unsealed objects allow new properties to be written at any time. Flow ensures
that reads are compatible with writes, but does not ensure that writes happen
before reads (in the order of execution).
This means that reads from unsealed objects with no matching writes are never
checked. This is an unsafe behavior of Flow which may be improved in the
future.
1
2
3
4
5
6
7
8
|
var obj = {};
obj.foo = 1;
obj.bar = true;
var foo: number = obj.foo;
var bar: boolean = obj.bar;
var baz: string = obj.baz;
|
{"value":"var obj = {};\n\nobj.foo = 1;\nobj.bar = true;\n\nvar foo: number = obj.foo; // Works!\nvar bar: boolean = obj.bar; // Works!\nvar baz: string = obj.baz; // Works?\n","tokens":[{"type":"T_VAR","context":"normal","value":"var","line":1,"start":0,"end":3},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":1,"start":4,"end":7},{"type":"T_ASSIGN","context":"normal","value":"=","line":1,"start":8,"end":9},{"type":"T_LCURLY","context":"normal","value":"{","line":1,"start":10,"end":11},{"type":"T_RCURLY","context":"normal","value":"}","line":1,"start":11,"end":12},{"type":"T_SEMICOLON","context":"normal","value":";","line":1,"start":12,"end":13},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":3,"start":15,"end":18},{"type":"T_PERIOD","context":"normal","value":".","line":3,"start":18,"end":19},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":3,"start":19,"end":22},{"type":"T_ASSIGN","context":"normal","value":"=","line":3,"start":23,"end":24},{"type":"T_NUMBER","context":"normal","value":"1","line":3,"start":25,"end":26},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":26,"end":27},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":4,"start":28,"end":31},{"type":"T_PERIOD","context":"normal","value":".","line":4,"start":31,"end":32},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":4,"start":32,"end":35},{"type":"T_ASSIGN","context":"normal","value":"=","line":4,"start":36,"end":37},{"type":"T_TRUE","context":"normal","value":"true","line":4,"start":38,"end":42},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":42,"end":43},{"type":"T_VAR","context":"normal","value":"var","line":6,"start":45,"end":48},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":6,"start":49,"end":52},{"type":"T_COLON","context":"type","value":":","line":6,"start":52,"end":53},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":6,"start":54,"end":60},{"type":"T_ASSIGN","context":"normal","value":"=","line":6,"start":62,"end":63},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":6,"start":64,"end":67},{"type":"T_PERIOD","context":"normal","value":".","line":6,"start":67,"end":68},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":6,"start":68,"end":71},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":71,"end":72},{"type":"Line","context":"comment","value":"// Works!","line":6,"start":73,"end":82},{"type":"T_VAR","context":"normal","value":"var","line":7,"start":83,"end":86},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":7,"start":87,"end":90},{"type":"T_COLON","context":"type","value":":","line":7,"start":90,"end":91},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":7,"start":92,"end":99},{"type":"T_ASSIGN","context":"normal","value":"=","line":7,"start":100,"end":101},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":7,"start":102,"end":105},{"type":"T_PERIOD","context":"normal","value":".","line":7,"start":105,"end":106},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":7,"start":106,"end":109},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":109,"end":110},{"type":"Line","context":"comment","value":"// Works!","line":7,"start":111,"end":120},{"type":"T_VAR","context":"normal","value":"var","line":8,"start":121,"end":124},{"type":"T_IDENTIFIER","context":"normal","value":"baz","line":8,"start":125,"end":128},{"type":"T_COLON","context":"type","value":":","line":8,"start":128,"end":129},{"type":"T_STRING_TYPE","context":"type","value":"string","line":8,"start":130,"end":136},{"type":"T_ASSIGN","context":"normal","value":"=","line":8,"start":138,"end":139},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":8,"start":140,"end":143},{"type":"T_PERIOD","context":"normal","value":".","line":8,"start":143,"end":144},{"type":"T_IDENTIFIER","context":"normal","value":"baz","line":8,"start":144,"end":147},{"type":"T_SEMICOLON","context":"normal","value":";","line":8,"start":147,"end":148},{"type":"Line","context":"comment","value":"// Works?","line":8,"start":149,"end":158}],"errors":[]}
Exact object types
In Flow, it is considered safe to pass an object with extra properties where
a normal object type is expected.
1
2
3
4
5
6
7
8
9
|
function method(obj: { foo: string }) {
}
method({
foo: "test",
bar: 42
});
|
{"value":"// @flow\nfunction method(obj: { foo: string }) {\n // ...\n}\n\nmethod({\n foo: \"test\", // Works!\n bar: 42 // Works!\n});\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_FUNCTION","context":"normal","value":"function","line":2,"start":9,"end":17},{"type":"T_IDENTIFIER","context":"normal","value":"method","line":2,"start":18,"end":24},{"type":"T_LPAREN","context":"normal","value":"(","line":2,"start":24,"end":25},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":2,"start":25,"end":28},{"type":"T_COLON","context":"type","value":":","line":2,"start":28,"end":29},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":30,"end":31},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":2,"start":32,"end":35},{"type":"T_COLON","context":"type","value":":","line":2,"start":35,"end":36},{"type":"T_STRING_TYPE","context":"type","value":"string","line":2,"start":37,"end":43},{"type":"T_RCURLY","context":"type","value":"}","line":2,"start":44,"end":45},{"type":"T_RPAREN","context":"normal","value":")","line":2,"start":45,"end":46},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":47,"end":48},{"type":"Line","context":"comment","value":"// ...","line":3,"start":51,"end":57},{"type":"T_RCURLY","context":"normal","value":"}","line":4,"start":58,"end":59},{"type":"T_IDENTIFIER","context":"normal","value":"method","line":6,"start":61,"end":67},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":67,"end":68},{"type":"T_LCURLY","context":"normal","value":"{","line":6,"start":68,"end":69},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":7,"start":72,"end":75},{"type":"T_COLON","context":"normal","value":":","line":7,"start":75,"end":76},{"type":"T_STRING","context":"normal","value":"\"test\"","line":7,"start":77,"end":83},{"type":"T_COMMA","context":"normal","value":",","line":7,"start":83,"end":84},{"type":"Line","context":"comment","value":"// Works!","line":7,"start":85,"end":94},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":8,"start":97,"end":100},{"type":"T_COLON","context":"normal","value":":","line":8,"start":100,"end":101},{"type":"T_NUMBER","context":"normal","value":"42","line":8,"start":102,"end":104},{"type":"Line","context":"comment","value":"// Works!","line":8,"start":110,"end":119},{"type":"T_RCURLY","context":"normal","value":"}","line":9,"start":120,"end":121},{"type":"T_RPAREN","context":"normal","value":")","line":9,"start":121,"end":122},{"type":"T_SEMICOLON","context":"normal","value":";","line":9,"start":122,"end":123}],"errors":[]}
Note: This is because of “width subtyping”.
Sometimes it is useful to disable this behavior and only allow a specific set
of properties. For this, Flow supports “exact” object types.
1
|
{| foo: string, bar: number |}
|
{"value":"{| foo: string, bar: number |}\n","tokens":[{"type":"T_LCURLY","context":"normal","value":"{","line":1,"start":0,"end":1},{"type":"T_BIT_OR","context":"normal","value":"|","line":1,"start":1,"end":2},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":1,"start":3,"end":6},{"type":"T_COLON","context":"normal","value":":","line":1,"start":6,"end":7},{"type":"T_IDENTIFIER","context":"normal","value":"string","line":1,"start":8,"end":14},{"type":"T_COMMA","context":"normal","value":",","line":1,"start":14,"end":15},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":1,"start":16,"end":19},{"type":"T_COLON","context":"normal","value":":","line":1,"start":19,"end":20},{"type":"T_IDENTIFIER","context":"normal","value":"number","line":1,"start":21,"end":27},{"type":"T_BIT_OR","context":"normal","value":"|","line":1,"start":28,"end":29},{"type":"T_RCURLY","context":"normal","value":"}","line":1,"start":29,"end":30}],"errors":[]}
Unlike regular object types, it is not valid to pass an object with “extra”
properties to an exact object type.
1
2
|
var foo: {| foo: string |} = { foo: "Hello", bar: "World!" };
|
Cannot assign object literal to `foo` because property `bar` is missing in object type [1] but exists in object literal [2]. [prop-missing]
{"value":"// @flow\nvar foo: {| foo: string |} = { foo: \"Hello\", bar: \"World!\" }; // Error!\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_VAR","context":"normal","value":"var","line":2,"start":9,"end":12},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":2,"start":13,"end":16},{"type":"T_COLON","context":"type","value":":","line":2,"start":16,"end":17},{"type":"T_LCURLYBAR","context":"type","value":"{|","line":2,"start":18,"end":20},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":2,"start":21,"end":24},{"type":"T_COLON","context":"type","value":":","line":2,"start":24,"end":25},{"type":"T_STRING_TYPE","context":"type","value":"string","line":2,"start":26,"end":32},{"type":"T_RCURLYBAR","context":"type","value":"|}","line":2,"start":33,"end":35},{"type":"T_ASSIGN","context":"normal","value":"=","line":2,"start":36,"end":37},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":38,"end":39},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":2,"start":40,"end":43},{"type":"T_COLON","context":"normal","value":":","line":2,"start":43,"end":44},{"type":"T_STRING","context":"normal","value":"\"Hello\"","line":2,"start":45,"end":52},{"type":"T_COMMA","context":"normal","value":",","line":2,"start":52,"end":53},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":2,"start":54,"end":57},{"type":"T_COLON","context":"normal","value":":","line":2,"start":57,"end":58},{"type":"T_STRING","context":"normal","value":"\"World!\"","line":2,"start":59,"end":67},{"type":"T_RCURLY","context":"normal","value":"}","line":2,"start":68,"end":69},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":69,"end":70},{"type":"Line","context":"comment","value":"// Error!","line":2,"start":71,"end":80}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot assign object literal to `foo` because property `bar` is missing in object type [1] but exists in object literal [2]. [prop-missing]","context":"var foo: {| foo: string |} = { foo: \"Hello\", bar: \"World!\" }; // Error!","source":"-","start":{"line":2,"column":30,"offset":38},"end":{"line":2,"column":60,"offset":69}}],"operation":null}]}
Intersections of exact object types may not work as you expect. If you need to combine exact object types, use object type spread:
1
2
3
4
5
6
7
8
9
10
|
type FooT = {| foo: string |};
type BarT = {| bar: number |};
type FooBarFailT = FooT & BarT;
type FooBarT = {| ...FooT, ...BarT |};
const fooBarFail: FooBarFailT = { foo: '123', bar: 12 };
const fooBar: FooBarT = { foo: '123', bar: 12 };
|
Cannot assign object literal to `fooBarFail` because property `bar` is missing in `FooT` [1] but exists in object literal [2]. [prop-missing]
Cannot assign object literal to `fooBarFail` because property `foo` is missing in `BarT` [1] but exists in object literal [2]. [prop-missing]
{"value":"// @flow\n\ntype FooT = {| foo: string |};\ntype BarT = {| bar: number |};\n\ntype FooBarFailT = FooT & BarT;\ntype FooBarT = {| ...FooT, ...BarT |};\n\nconst fooBarFail: FooBarFailT = { foo: '123', bar: 12 }; // Error!\nconst fooBar: FooBarT = { foo: '123', bar: 12 }; // Works!\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_TYPE","context":"normal","value":"type","line":3,"start":10,"end":14},{"type":"T_IDENTIFIER","context":"type","value":"FooT","line":3,"start":15,"end":19},{"type":"T_ASSIGN","context":"type","value":"=","line":3,"start":20,"end":21},{"type":"T_LCURLYBAR","context":"type","value":"{|","line":3,"start":22,"end":24},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":3,"start":25,"end":28},{"type":"T_COLON","context":"type","value":":","line":3,"start":28,"end":29},{"type":"T_STRING_TYPE","context":"type","value":"string","line":3,"start":30,"end":36},{"type":"T_RCURLYBAR","context":"type","value":"|}","line":3,"start":37,"end":39},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":39,"end":40},{"type":"T_TYPE","context":"normal","value":"type","line":4,"start":41,"end":45},{"type":"T_IDENTIFIER","context":"type","value":"BarT","line":4,"start":46,"end":50},{"type":"T_ASSIGN","context":"type","value":"=","line":4,"start":51,"end":52},{"type":"T_LCURLYBAR","context":"type","value":"{|","line":4,"start":53,"end":55},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":4,"start":56,"end":59},{"type":"T_COLON","context":"type","value":":","line":4,"start":59,"end":60},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":4,"start":61,"end":67},{"type":"T_RCURLYBAR","context":"type","value":"|}","line":4,"start":68,"end":70},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":70,"end":71},{"type":"T_TYPE","context":"normal","value":"type","line":6,"start":73,"end":77},{"type":"T_IDENTIFIER","context":"type","value":"FooBarFailT","line":6,"start":78,"end":89},{"type":"T_ASSIGN","context":"type","value":"=","line":6,"start":90,"end":91},{"type":"T_IDENTIFIER","context":"type","value":"FooT","line":6,"start":92,"end":96},{"type":"T_BIT_AND","context":"type","value":"&","line":6,"start":97,"end":98},{"type":"T_IDENTIFIER","context":"type","value":"BarT","line":6,"start":99,"end":103},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":103,"end":104},{"type":"T_TYPE","context":"normal","value":"type","line":7,"start":105,"end":109},{"type":"T_IDENTIFIER","context":"type","value":"FooBarT","line":7,"start":110,"end":117},{"type":"T_ASSIGN","context":"type","value":"=","line":7,"start":118,"end":119},{"type":"T_LCURLYBAR","context":"type","value":"{|","line":7,"start":120,"end":122},{"type":"T_ELLIPSIS","context":"type","value":"...","line":7,"start":123,"end":126},{"type":"T_IDENTIFIER","context":"type","value":"FooT","line":7,"start":126,"end":130},{"type":"T_COMMA","context":"type","value":",","line":7,"start":130,"end":131},{"type":"T_ELLIPSIS","context":"type","value":"...","line":7,"start":132,"end":135},{"type":"T_IDENTIFIER","context":"type","value":"BarT","line":7,"start":135,"end":139},{"type":"T_RCURLYBAR","context":"type","value":"|}","line":7,"start":140,"end":142},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":142,"end":143},{"type":"T_CONST","context":"normal","value":"const","line":9,"start":145,"end":150},{"type":"T_IDENTIFIER","context":"normal","value":"fooBarFail","line":9,"start":151,"end":161},{"type":"T_COLON","context":"type","value":":","line":9,"start":161,"end":162},{"type":"T_IDENTIFIER","context":"type","value":"FooBarFailT","line":9,"start":163,"end":174},{"type":"T_ASSIGN","context":"normal","value":"=","line":9,"start":175,"end":176},{"type":"T_LCURLY","context":"normal","value":"{","line":9,"start":177,"end":178},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":9,"start":179,"end":182},{"type":"T_COLON","context":"normal","value":":","line":9,"start":182,"end":183},{"type":"T_STRING","context":"normal","value":"'123'","line":9,"start":184,"end":189},{"type":"T_COMMA","context":"normal","value":",","line":9,"start":189,"end":190},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":9,"start":191,"end":194},{"type":"T_COLON","context":"normal","value":":","line":9,"start":194,"end":195},{"type":"T_NUMBER","context":"normal","value":"12","line":9,"start":196,"end":198},{"type":"T_RCURLY","context":"normal","value":"}","line":9,"start":199,"end":200},{"type":"T_SEMICOLON","context":"normal","value":";","line":9,"start":200,"end":201},{"type":"Line","context":"comment","value":"// Error!","line":9,"start":202,"end":211},{"type":"T_CONST","context":"normal","value":"const","line":10,"start":212,"end":217},{"type":"T_IDENTIFIER","context":"normal","value":"fooBar","line":10,"start":218,"end":224},{"type":"T_COLON","context":"type","value":":","line":10,"start":224,"end":225},{"type":"T_IDENTIFIER","context":"type","value":"FooBarT","line":10,"start":226,"end":233},{"type":"T_ASSIGN","context":"normal","value":"=","line":10,"start":234,"end":235},{"type":"T_LCURLY","context":"normal","value":"{","line":10,"start":236,"end":237},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":10,"start":238,"end":241},{"type":"T_COLON","context":"normal","value":":","line":10,"start":241,"end":242},{"type":"T_STRING","context":"normal","value":"'123'","line":10,"start":243,"end":248},{"type":"T_COMMA","context":"normal","value":",","line":10,"start":248,"end":249},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":10,"start":250,"end":253},{"type":"T_COLON","context":"normal","value":":","line":10,"start":253,"end":254},{"type":"T_NUMBER","context":"normal","value":"12","line":10,"start":255,"end":257},{"type":"T_RCURLY","context":"normal","value":"}","line":10,"start":258,"end":259},{"type":"T_SEMICOLON","context":"normal","value":";","line":10,"start":259,"end":260},{"type":"Line","context":"comment","value":"// Works!","line":10,"start":261,"end":270}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot assign object literal to `fooBarFail` because property `bar` is missing in `FooT` [1] but exists in object literal [2]. [prop-missing]","context":"const fooBarFail: FooBarFailT = { foo: '123', bar: 12 }; // Error!","source":"-","start":{"line":9,"column":33,"offset":177},"end":{"line":9,"column":55,"offset":200}}],"operation":null},{"id":"E2","messages":[{"id":"E2M1","description":"Cannot assign object literal to `fooBarFail` because property `foo` is missing in `BarT` [1] but exists in object literal [2]. [prop-missing]","context":"const fooBarFail: FooBarFailT = { foo: '123', bar: 12 }; // Error!","source":"-","start":{"line":9,"column":33,"offset":177},"end":{"line":9,"column":55,"offset":200}}],"operation":null}]}
Explicit inexact object types
In addition to the default {}
syntax, you can explicitly indicate an inexact
object by using an ellipsis at the end of your property list:
1
2
3
|
type Inexact = {foo: number, ...};
|
{"value":"// @flow\n\ntype Inexact = {foo: number, ...};\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_TYPE","context":"normal","value":"type","line":3,"start":10,"end":14},{"type":"T_IDENTIFIER","context":"type","value":"Inexact","line":3,"start":15,"end":22},{"type":"T_ASSIGN","context":"type","value":"=","line":3,"start":23,"end":24},{"type":"T_LCURLY","context":"type","value":"{","line":3,"start":25,"end":26},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":3,"start":26,"end":29},{"type":"T_COLON","context":"type","value":":","line":3,"start":29,"end":30},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":3,"start":31,"end":37},{"type":"T_COMMA","context":"type","value":",","line":3,"start":37,"end":38},{"type":"T_ELLIPSIS","context":"type","value":"...","line":3,"start":39,"end":42},{"type":"T_RCURLY","context":"type","value":"}","line":3,"start":42,"end":43},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":43,"end":44}],"errors":[]}
Flow is planning to make object types exact by default.
This is available via an option in your flowconfig.
You can also read our upgrade guide
for steps to enable this option in your own project.
In a project using exact-by-default syntax, the explicit inexact object type syntax is the only way to express an inexact object type.
Objects as maps
Newer versions of the JavaScript standard include a Map
class, but it is
still very common to use objects as maps as well. In this use case, an object
will likely have properties added to it and retrieved throughout its lifecycle.
Furthermore, the property keys may not even be known statically, so writing out
a type annotation would not be possible.
For objects like these, Flow provides a special kind of property, called an
“indexer property.” An indexer property allows reads and writes using any key
that matches the indexer key type.
1
2
3
4
5
|
var o: { [string]: number } = {};
o["foo"] = 0;
o["bar"] = 1;
var foo: number = o["foo"];
|
{"value":"// @flow\nvar o: { [string]: number } = {};\no[\"foo\"] = 0;\no[\"bar\"] = 1;\nvar foo: number = o[\"foo\"];\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_VAR","context":"normal","value":"var","line":2,"start":9,"end":12},{"type":"T_IDENTIFIER","context":"normal","value":"o","line":2,"start":13,"end":14},{"type":"T_COLON","context":"type","value":":","line":2,"start":14,"end":15},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":16,"end":17},{"type":"T_LBRACKET","context":"type","value":"[","line":2,"start":18,"end":19},{"type":"T_STRING_TYPE","context":"type","value":"string","line":2,"start":19,"end":25},{"type":"T_RBRACKET","context":"type","value":"]","line":2,"start":25,"end":26},{"type":"T_COLON","context":"type","value":":","line":2,"start":26,"end":27},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":2,"start":28,"end":34},{"type":"T_RCURLY","context":"type","value":"}","line":2,"start":35,"end":36},{"type":"T_ASSIGN","context":"normal","value":"=","line":2,"start":37,"end":38},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":39,"end":40},{"type":"T_RCURLY","context":"normal","value":"}","line":2,"start":40,"end":41},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":41,"end":42},{"type":"T_IDENTIFIER","context":"normal","value":"o","line":3,"start":43,"end":44},{"type":"T_LBRACKET","context":"normal","value":"[","line":3,"start":44,"end":45},{"type":"T_STRING","context":"normal","value":"\"foo\"","line":3,"start":45,"end":50},{"type":"T_RBRACKET","context":"normal","value":"]","line":3,"start":50,"end":51},{"type":"T_ASSIGN","context":"normal","value":"=","line":3,"start":52,"end":53},{"type":"T_NUMBER","context":"normal","value":"0","line":3,"start":54,"end":55},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":55,"end":56},{"type":"T_IDENTIFIER","context":"normal","value":"o","line":4,"start":57,"end":58},{"type":"T_LBRACKET","context":"normal","value":"[","line":4,"start":58,"end":59},{"type":"T_STRING","context":"normal","value":"\"bar\"","line":4,"start":59,"end":64},{"type":"T_RBRACKET","context":"normal","value":"]","line":4,"start":64,"end":65},{"type":"T_ASSIGN","context":"normal","value":"=","line":4,"start":66,"end":67},{"type":"T_NUMBER","context":"normal","value":"1","line":4,"start":68,"end":69},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":69,"end":70},{"type":"T_VAR","context":"normal","value":"var","line":5,"start":71,"end":74},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":5,"start":75,"end":78},{"type":"T_COLON","context":"type","value":":","line":5,"start":78,"end":79},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":5,"start":80,"end":86},{"type":"T_ASSIGN","context":"normal","value":"=","line":5,"start":87,"end":88},{"type":"T_IDENTIFIER","context":"normal","value":"o","line":5,"start":89,"end":90},{"type":"T_LBRACKET","context":"normal","value":"[","line":5,"start":90,"end":91},{"type":"T_STRING","context":"normal","value":"\"foo\"","line":5,"start":91,"end":96},{"type":"T_RBRACKET","context":"normal","value":"]","line":5,"start":96,"end":97},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":97,"end":98}],"errors":[]}
An indexer can be optionally named, for documentation purposes:
1
2
3
4
5
6
|
var obj: { [user_id: number]: string } = {};
obj[1] = "Julia";
obj[2] = "Camille";
obj[3] = "Justin";
obj[4] = "Mark";
|
{"value":"// @flow\nvar obj: { [user_id: number]: string } = {};\nobj[1] = \"Julia\";\nobj[2] = \"Camille\";\nobj[3] = \"Justin\";\nobj[4] = \"Mark\";\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_VAR","context":"normal","value":"var","line":2,"start":9,"end":12},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":2,"start":13,"end":16},{"type":"T_COLON","context":"type","value":":","line":2,"start":16,"end":17},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":18,"end":19},{"type":"T_LBRACKET","context":"type","value":"[","line":2,"start":20,"end":21},{"type":"T_IDENTIFIER","context":"type","value":"user_id","line":2,"start":21,"end":28},{"type":"T_COLON","context":"type","value":":","line":2,"start":28,"end":29},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":2,"start":30,"end":36},{"type":"T_RBRACKET","context":"type","value":"]","line":2,"start":36,"end":37},{"type":"T_COLON","context":"type","value":":","line":2,"start":37,"end":38},{"type":"T_STRING_TYPE","context":"type","value":"string","line":2,"start":39,"end":45},{"type":"T_RCURLY","context":"type","value":"}","line":2,"start":46,"end":47},{"type":"T_ASSIGN","context":"normal","value":"=","line":2,"start":48,"end":49},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":50,"end":51},{"type":"T_RCURLY","context":"normal","value":"}","line":2,"start":51,"end":52},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":52,"end":53},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":3,"start":54,"end":57},{"type":"T_LBRACKET","context":"normal","value":"[","line":3,"start":57,"end":58},{"type":"T_NUMBER","context":"normal","value":"1","line":3,"start":58,"end":59},{"type":"T_RBRACKET","context":"normal","value":"]","line":3,"start":59,"end":60},{"type":"T_ASSIGN","context":"normal","value":"=","line":3,"start":61,"end":62},{"type":"T_STRING","context":"normal","value":"\"Julia\"","line":3,"start":63,"end":70},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":70,"end":71},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":4,"start":72,"end":75},{"type":"T_LBRACKET","context":"normal","value":"[","line":4,"start":75,"end":76},{"type":"T_NUMBER","context":"normal","value":"2","line":4,"start":76,"end":77},{"type":"T_RBRACKET","context":"normal","value":"]","line":4,"start":77,"end":78},{"type":"T_ASSIGN","context":"normal","value":"=","line":4,"start":79,"end":80},{"type":"T_STRING","context":"normal","value":"\"Camille\"","line":4,"start":81,"end":90},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":90,"end":91},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":5,"start":92,"end":95},{"type":"T_LBRACKET","context":"normal","value":"[","line":5,"start":95,"end":96},{"type":"T_NUMBER","context":"normal","value":"3","line":5,"start":96,"end":97},{"type":"T_RBRACKET","context":"normal","value":"]","line":5,"start":97,"end":98},{"type":"T_ASSIGN","context":"normal","value":"=","line":5,"start":99,"end":100},{"type":"T_STRING","context":"normal","value":"\"Justin\"","line":5,"start":101,"end":109},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":109,"end":110},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":6,"start":111,"end":114},{"type":"T_LBRACKET","context":"normal","value":"[","line":6,"start":114,"end":115},{"type":"T_NUMBER","context":"normal","value":"4","line":6,"start":115,"end":116},{"type":"T_RBRACKET","context":"normal","value":"]","line":6,"start":116,"end":117},{"type":"T_ASSIGN","context":"normal","value":"=","line":6,"start":118,"end":119},{"type":"T_STRING","context":"normal","value":"\"Mark\"","line":6,"start":120,"end":126},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":126,"end":127}],"errors":[]}
When an object type has an indexer property, property accesses are assumed to
have the annotated type, even if the object does not have a value in that slot
at runtime. It is the programmer’s responsibility to ensure the access is safe,
as with arrays.
1
2
|
var obj: { [number]: string } = {};
obj[42].length;
|
{"value":"var obj: { [number]: string } = {};\nobj[42].length; // No type error, but will throw at runtime\n","tokens":[{"type":"T_VAR","context":"normal","value":"var","line":1,"start":0,"end":3},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":1,"start":4,"end":7},{"type":"T_COLON","context":"type","value":":","line":1,"start":7,"end":8},{"type":"T_LCURLY","context":"type","value":"{","line":1,"start":9,"end":10},{"type":"T_LBRACKET","context":"type","value":"[","line":1,"start":11,"end":12},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":1,"start":12,"end":18},{"type":"T_RBRACKET","context":"type","value":"]","line":1,"start":18,"end":19},{"type":"T_COLON","context":"type","value":":","line":1,"start":19,"end":20},{"type":"T_STRING_TYPE","context":"type","value":"string","line":1,"start":21,"end":27},{"type":"T_RCURLY","context":"type","value":"}","line":1,"start":28,"end":29},{"type":"T_ASSIGN","context":"normal","value":"=","line":1,"start":30,"end":31},{"type":"T_LCURLY","context":"normal","value":"{","line":1,"start":32,"end":33},{"type":"T_RCURLY","context":"normal","value":"}","line":1,"start":33,"end":34},{"type":"T_SEMICOLON","context":"normal","value":";","line":1,"start":34,"end":35},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":2,"start":36,"end":39},{"type":"T_LBRACKET","context":"normal","value":"[","line":2,"start":39,"end":40},{"type":"T_NUMBER","context":"normal","value":"42","line":2,"start":40,"end":42},{"type":"T_RBRACKET","context":"normal","value":"]","line":2,"start":42,"end":43},{"type":"T_PERIOD","context":"normal","value":".","line":2,"start":43,"end":44},{"type":"T_IDENTIFIER","context":"normal","value":"length","line":2,"start":44,"end":50},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":50,"end":51},{"type":"Line","context":"comment","value":"// No type error, but will throw at runtime","line":2,"start":52,"end":95}],"errors":[]}
Indexer properties can be mixed with named properties:
1
2
3
4
5
6
7
8
9
10
11
12
|
var obj: {
size: number,
[id: number]: string
} = {
size: 0
};
function add(id: number, name: string) {
obj[id] = name;
obj.size++;
}
|
{"value":"// @flow\nvar obj: {\n size: number,\n [id: number]: string\n} = {\n size: 0\n};\n\nfunction add(id: number, name: string) {\n obj[id] = name;\n obj.size++;\n}\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_VAR","context":"normal","value":"var","line":2,"start":9,"end":12},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":2,"start":13,"end":16},{"type":"T_COLON","context":"type","value":":","line":2,"start":16,"end":17},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":18,"end":19},{"type":"T_IDENTIFIER","context":"normal","value":"size","line":3,"start":22,"end":26},{"type":"T_COLON","context":"type","value":":","line":3,"start":26,"end":27},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":3,"start":28,"end":34},{"type":"T_COMMA","context":"type","value":",","line":3,"start":34,"end":35},{"type":"T_LBRACKET","context":"type","value":"[","line":4,"start":38,"end":39},{"type":"T_IDENTIFIER","context":"type","value":"id","line":4,"start":39,"end":41},{"type":"T_COLON","context":"type","value":":","line":4,"start":41,"end":42},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":4,"start":43,"end":49},{"type":"T_RBRACKET","context":"type","value":"]","line":4,"start":49,"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":5,"start":59,"end":60},{"type":"T_ASSIGN","context":"normal","value":"=","line":5,"start":61,"end":62},{"type":"T_LCURLY","context":"normal","value":"{","line":5,"start":63,"end":64},{"type":"T_IDENTIFIER","context":"normal","value":"size","line":6,"start":67,"end":71},{"type":"T_COLON","context":"normal","value":":","line":6,"start":71,"end":72},{"type":"T_NUMBER","context":"normal","value":"0","line":6,"start":73,"end":74},{"type":"T_RCURLY","context":"normal","value":"}","line":7,"start":75,"end":76},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":76,"end":77},{"type":"T_FUNCTION","context":"normal","value":"function","line":9,"start":79,"end":87},{"type":"T_IDENTIFIER","context":"normal","value":"add","line":9,"start":88,"end":91},{"type":"T_LPAREN","context":"normal","value":"(","line":9,"start":91,"end":92},{"type":"T_IDENTIFIER","context":"normal","value":"id","line":9,"start":92,"end":94},{"type":"T_COLON","context":"type","value":":","line":9,"start":94,"end":95},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":9,"start":96,"end":102},{"type":"T_COMMA","context":"normal","value":",","line":9,"start":102,"end":103},{"type":"T_IDENTIFIER","context":"normal","value":"name","line":9,"start":104,"end":108},{"type":"T_COLON","context":"type","value":":","line":9,"start":108,"end":109},{"type":"T_STRING_TYPE","context":"type","value":"string","line":9,"start":110,"end":116},{"type":"T_RPAREN","context":"normal","value":")","line":9,"start":116,"end":117},{"type":"T_LCURLY","context":"normal","value":"{","line":9,"start":118,"end":119},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":10,"start":122,"end":125},{"type":"T_LBRACKET","context":"normal","value":"[","line":10,"start":125,"end":126},{"type":"T_IDENTIFIER","context":"normal","value":"id","line":10,"start":126,"end":128},{"type":"T_RBRACKET","context":"normal","value":"]","line":10,"start":128,"end":129},{"type":"T_ASSIGN","context":"normal","value":"=","line":10,"start":130,"end":131},{"type":"T_IDENTIFIER","context":"normal","value":"name","line":10,"start":132,"end":136},{"type":"T_SEMICOLON","context":"normal","value":";","line":10,"start":136,"end":137},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":11,"start":140,"end":143},{"type":"T_PERIOD","context":"normal","value":".","line":11,"start":143,"end":144},{"type":"T_IDENTIFIER","context":"normal","value":"size","line":11,"start":144,"end":148},{"type":"T_INCR","context":"normal","value":"++","line":11,"start":148,"end":150},{"type":"T_SEMICOLON","context":"normal","value":";","line":11,"start":150,"end":151},{"type":"T_RCURLY","context":"normal","value":"}","line":12,"start":152,"end":153}],"errors":[]}
Object
Type
NOTE: For new code, prefer any
or { [key: string]: any}
. Object
is an alias to any
and will
be deprecated and removed in a future version of Flow.
Sometimes it is useful to write types that accept arbitrary objects, for
those you should write {}
like this:
1
2
3
|
function method(obj: {}) {
}
|
{"value":"function method(obj: {}) {\n // ...\n}\n","tokens":[{"type":"T_FUNCTION","context":"normal","value":"function","line":1,"start":0,"end":8},{"type":"T_IDENTIFIER","context":"normal","value":"method","line":1,"start":9,"end":15},{"type":"T_LPAREN","context":"normal","value":"(","line":1,"start":15,"end":16},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":1,"start":16,"end":19},{"type":"T_COLON","context":"type","value":":","line":1,"start":19,"end":20},{"type":"T_LCURLY","context":"type","value":"{","line":1,"start":21,"end":22},{"type":"T_RCURLY","context":"type","value":"}","line":1,"start":22,"end":23},{"type":"T_RPAREN","context":"normal","value":")","line":1,"start":23,"end":24},{"type":"T_LCURLY","context":"normal","value":"{","line":1,"start":25,"end":26},{"type":"Line","context":"comment","value":"// ...","line":2,"start":29,"end":35},{"type":"T_RCURLY","context":"normal","value":"}","line":3,"start":36,"end":37}],"errors":[]}
However, if you need to opt-out of the type checker, and don’t want to go all
the way to any
, you could use { [key: string]: any}
. (Note that any
is unsafe and
should be avoided). For historical reasons, the Object
keyword is still available.
In previous versions of Flow, Object
was the same
as { [key: string]: any}
.
For example, the following code will not report any errors:
1
2
3
4
5
6
7
|
function method(obj: { [key: string]: any }) {
obj.foo = 42;
let bar: boolean = obj.bar;
obj.baz.bat.bam.bop;
}
method({ baz: 3.14, bar: "hello" });
|
{"value":"function method(obj: { [key: string]: any }) {\n obj.foo = 42; // Works.\n let bar: boolean = obj.bar; // Works.\n obj.baz.bat.bam.bop; // Works.\n}\n\nmethod({ baz: 3.14, bar: \"hello\" });\n","tokens":[{"type":"T_FUNCTION","context":"normal","value":"function","line":1,"start":0,"end":8},{"type":"T_IDENTIFIER","context":"normal","value":"method","line":1,"start":9,"end":15},{"type":"T_LPAREN","context":"normal","value":"(","line":1,"start":15,"end":16},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":1,"start":16,"end":19},{"type":"T_COLON","context":"type","value":":","line":1,"start":19,"end":20},{"type":"T_LCURLY","context":"type","value":"{","line":1,"start":21,"end":22},{"type":"T_LBRACKET","context":"type","value":"[","line":1,"start":23,"end":24},{"type":"T_IDENTIFIER","context":"type","value":"key","line":1,"start":24,"end":27},{"type":"T_COLON","context":"type","value":":","line":1,"start":27,"end":28},{"type":"T_STRING_TYPE","context":"type","value":"string","line":1,"start":29,"end":35},{"type":"T_RBRACKET","context":"type","value":"]","line":1,"start":35,"end":36},{"type":"T_COLON","context":"type","value":":","line":1,"start":36,"end":37},{"type":"T_ANY_TYPE","context":"type","value":"any","line":1,"start":38,"end":41},{"type":"T_RCURLY","context":"type","value":"}","line":1,"start":42,"end":43},{"type":"T_RPAREN","context":"normal","value":")","line":1,"start":43,"end":44},{"type":"T_LCURLY","context":"normal","value":"{","line":1,"start":45,"end":46},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":2,"start":49,"end":52},{"type":"T_PERIOD","context":"normal","value":".","line":2,"start":52,"end":53},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":2,"start":53,"end":56},{"type":"T_ASSIGN","context":"normal","value":"=","line":2,"start":57,"end":58},{"type":"T_NUMBER","context":"normal","value":"42","line":2,"start":59,"end":61},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":61,"end":62},{"type":"Line","context":"comment","value":"// Works.","line":2,"start":77,"end":86},{"type":"T_LET","context":"normal","value":"let","line":3,"start":89,"end":92},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":3,"start":93,"end":96},{"type":"T_COLON","context":"type","value":":","line":3,"start":96,"end":97},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":3,"start":98,"end":105},{"type":"T_ASSIGN","context":"normal","value":"=","line":3,"start":106,"end":107},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":3,"start":108,"end":111},{"type":"T_PERIOD","context":"normal","value":".","line":3,"start":111,"end":112},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":3,"start":112,"end":115},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":115,"end":116},{"type":"Line","context":"comment","value":"// Works.","line":3,"start":117,"end":126},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":4,"start":129,"end":132},{"type":"T_PERIOD","context":"normal","value":".","line":4,"start":132,"end":133},{"type":"T_IDENTIFIER","context":"normal","value":"baz","line":4,"start":133,"end":136},{"type":"T_PERIOD","context":"normal","value":".","line":4,"start":136,"end":137},{"type":"T_IDENTIFIER","context":"normal","value":"bat","line":4,"start":137,"end":140},{"type":"T_PERIOD","context":"normal","value":".","line":4,"start":140,"end":141},{"type":"T_IDENTIFIER","context":"normal","value":"bam","line":4,"start":141,"end":144},{"type":"T_PERIOD","context":"normal","value":".","line":4,"start":144,"end":145},{"type":"T_IDENTIFIER","context":"normal","value":"bop","line":4,"start":145,"end":148},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":148,"end":149},{"type":"Line","context":"comment","value":"// Works.","line":4,"start":157,"end":166},{"type":"T_RCURLY","context":"normal","value":"}","line":5,"start":167,"end":168},{"type":"T_IDENTIFIER","context":"normal","value":"method","line":7,"start":170,"end":176},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":176,"end":177},{"type":"T_LCURLY","context":"normal","value":"{","line":7,"start":177,"end":178},{"type":"T_IDENTIFIER","context":"normal","value":"baz","line":7,"start":179,"end":182},{"type":"T_COLON","context":"normal","value":":","line":7,"start":182,"end":183},{"type":"T_NUMBER","context":"normal","value":"3.14","line":7,"start":184,"end":188},{"type":"T_COMMA","context":"normal","value":",","line":7,"start":188,"end":189},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":7,"start":190,"end":193},{"type":"T_COLON","context":"normal","value":":","line":7,"start":193,"end":194},{"type":"T_STRING","context":"normal","value":"\"hello\"","line":7,"start":195,"end":202},{"type":"T_RCURLY","context":"normal","value":"}","line":7,"start":203,"end":204},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":204,"end":205},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":205,"end":206}],"errors":[]}
Neither will this:
1
2
3
4
5
|
function method(obj: Object) {
obj = 10;
}
method({ baz: 3.14, bar: "hello" });
|
{"value":"function method(obj: Object) {\n obj = 10;\n}\n\nmethod({ baz: 3.14, bar: \"hello\" });\n","tokens":[{"type":"T_FUNCTION","context":"normal","value":"function","line":1,"start":0,"end":8},{"type":"T_IDENTIFIER","context":"normal","value":"method","line":1,"start":9,"end":15},{"type":"T_LPAREN","context":"normal","value":"(","line":1,"start":15,"end":16},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":1,"start":16,"end":19},{"type":"T_COLON","context":"type","value":":","line":1,"start":19,"end":20},{"type":"T_IDENTIFIER","context":"type","value":"Object","line":1,"start":21,"end":27},{"type":"T_RPAREN","context":"normal","value":")","line":1,"start":27,"end":28},{"type":"T_LCURLY","context":"normal","value":"{","line":1,"start":29,"end":30},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":2,"start":33,"end":36},{"type":"T_ASSIGN","context":"normal","value":"=","line":2,"start":37,"end":38},{"type":"T_NUMBER","context":"normal","value":"10","line":2,"start":39,"end":41},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":41,"end":42},{"type":"T_RCURLY","context":"normal","value":"}","line":3,"start":43,"end":44},{"type":"T_IDENTIFIER","context":"normal","value":"method","line":5,"start":46,"end":52},{"type":"T_LPAREN","context":"normal","value":"(","line":5,"start":52,"end":53},{"type":"T_LCURLY","context":"normal","value":"{","line":5,"start":53,"end":54},{"type":"T_IDENTIFIER","context":"normal","value":"baz","line":5,"start":55,"end":58},{"type":"T_COLON","context":"normal","value":":","line":5,"start":58,"end":59},{"type":"T_NUMBER","context":"normal","value":"3.14","line":5,"start":60,"end":64},{"type":"T_COMMA","context":"normal","value":",","line":5,"start":64,"end":65},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":5,"start":66,"end":69},{"type":"T_COLON","context":"normal","value":":","line":5,"start":69,"end":70},{"type":"T_STRING","context":"normal","value":"\"hello\"","line":5,"start":71,"end":78},{"type":"T_RCURLY","context":"normal","value":"}","line":5,"start":79,"end":80},{"type":"T_RPAREN","context":"normal","value":")","line":5,"start":80,"end":81},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":81,"end":82}],"errors":[]}