JavaScript has a number of different primitive types
(MDN):
- Booleans
- Strings
- Numbers
null
undefined
(void
in Flow types)
- Symbols (new in ECMAScript 2015)
The primitive types appear in the language as either literal values.
1
2
3
4
5
|
true;
"hello";
3.14;
null;
undefined;
|
{"value":"true;\n\"hello\";\n3.14;\nnull;\nundefined;\n","tokens":[{"type":"T_TRUE","context":"normal","value":"true","line":1,"start":0,"end":4},{"type":"T_SEMICOLON","context":"normal","value":";","line":1,"start":4,"end":5},{"type":"T_STRING","context":"normal","value":"\"hello\"","line":2,"start":6,"end":13},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":13,"end":14},{"type":"T_NUMBER","context":"normal","value":"3.14","line":3,"start":15,"end":19},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":19,"end":20},{"type":"T_NULL","context":"normal","value":"null","line":4,"start":21,"end":25},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":25,"end":26},{"type":"T_IDENTIFIER","context":"normal","value":"undefined","line":5,"start":27,"end":36},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":36,"end":37}],"errors":[]}
Or as constructed wrapper objects.
1
2
3
|
new Boolean(false);
new String("world");
new Number(42);
|
{"value":"new Boolean(false);\nnew String(\"world\");\nnew Number(42);\n","tokens":[{"type":"T_NEW","context":"normal","value":"new","line":1,"start":0,"end":3},{"type":"T_IDENTIFIER","context":"normal","value":"Boolean","line":1,"start":4,"end":11},{"type":"T_LPAREN","context":"normal","value":"(","line":1,"start":11,"end":12},{"type":"T_FALSE","context":"normal","value":"false","line":1,"start":12,"end":17},{"type":"T_RPAREN","context":"normal","value":")","line":1,"start":17,"end":18},{"type":"T_SEMICOLON","context":"normal","value":";","line":1,"start":18,"end":19},{"type":"T_NEW","context":"normal","value":"new","line":2,"start":20,"end":23},{"type":"T_IDENTIFIER","context":"normal","value":"String","line":2,"start":24,"end":30},{"type":"T_LPAREN","context":"normal","value":"(","line":2,"start":30,"end":31},{"type":"T_STRING","context":"normal","value":"\"world\"","line":2,"start":31,"end":38},{"type":"T_RPAREN","context":"normal","value":")","line":2,"start":38,"end":39},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":39,"end":40},{"type":"T_NEW","context":"normal","value":"new","line":3,"start":41,"end":44},{"type":"T_IDENTIFIER","context":"normal","value":"Number","line":3,"start":45,"end":51},{"type":"T_LPAREN","context":"normal","value":"(","line":3,"start":51,"end":52},{"type":"T_NUMBER","context":"normal","value":"42","line":3,"start":52,"end":54},{"type":"T_RPAREN","context":"normal","value":")","line":3,"start":54,"end":55},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":55,"end":56}],"errors":[]}
Types for literal values are lowercase.
1
2
3
4
5
6
|
function method(x: number, y: string, z: boolean) {
}
method(3.14, "hello", true);
|
{"value":"// @flow\nfunction method(x: number, y: string, z: boolean) {\n // ...\n}\n\nmethod(3.14, \"hello\", true);\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":"x","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_COMMA","context":"normal","value":",","line":2,"start":34,"end":35},{"type":"T_IDENTIFIER","context":"normal","value":"y","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_COMMA","context":"normal","value":",","line":2,"start":45,"end":46},{"type":"T_IDENTIFIER","context":"normal","value":"z","line":2,"start":47,"end":48},{"type":"T_COLON","context":"type","value":":","line":2,"start":48,"end":49},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":2,"start":50,"end":57},{"type":"T_RPAREN","context":"normal","value":")","line":2,"start":57,"end":58},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":59,"end":60},{"type":"Line","context":"comment","value":"// ...","line":3,"start":63,"end":69},{"type":"T_RCURLY","context":"normal","value":"}","line":4,"start":70,"end":71},{"type":"T_IDENTIFIER","context":"normal","value":"method","line":6,"start":73,"end":79},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":79,"end":80},{"type":"T_NUMBER","context":"normal","value":"3.14","line":6,"start":80,"end":84},{"type":"T_COMMA","context":"normal","value":",","line":6,"start":84,"end":85},{"type":"T_STRING","context":"normal","value":"\"hello\"","line":6,"start":86,"end":93},{"type":"T_COMMA","context":"normal","value":",","line":6,"start":93,"end":94},{"type":"T_TRUE","context":"normal","value":"true","line":6,"start":95,"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}],"errors":[]}
Types for the wrapper objects are capitalized (the same as their constructor).
1
2
3
4
5
6
|
function method(x: Number, y: String, z: Boolean) {
}
method(new Number(42), new String("world"), new Boolean(false));
|
{"value":"// @flow\nfunction method(x: Number, y: String, z: Boolean) {\n // ...\n}\n\nmethod(new Number(42), new String(\"world\"), new Boolean(false));\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":"x","line":2,"start":25,"end":26},{"type":"T_COLON","context":"type","value":":","line":2,"start":26,"end":27},{"type":"T_IDENTIFIER","context":"type","value":"Number","line":2,"start":28,"end":34},{"type":"T_COMMA","context":"normal","value":",","line":2,"start":34,"end":35},{"type":"T_IDENTIFIER","context":"normal","value":"y","line":2,"start":36,"end":37},{"type":"T_COLON","context":"type","value":":","line":2,"start":37,"end":38},{"type":"T_IDENTIFIER","context":"type","value":"String","line":2,"start":39,"end":45},{"type":"T_COMMA","context":"normal","value":",","line":2,"start":45,"end":46},{"type":"T_IDENTIFIER","context":"normal","value":"z","line":2,"start":47,"end":48},{"type":"T_COLON","context":"type","value":":","line":2,"start":48,"end":49},{"type":"T_IDENTIFIER","context":"type","value":"Boolean","line":2,"start":50,"end":57},{"type":"T_RPAREN","context":"normal","value":")","line":2,"start":57,"end":58},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":59,"end":60},{"type":"Line","context":"comment","value":"// ...","line":3,"start":63,"end":69},{"type":"T_RCURLY","context":"normal","value":"}","line":4,"start":70,"end":71},{"type":"T_IDENTIFIER","context":"normal","value":"method","line":6,"start":73,"end":79},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":79,"end":80},{"type":"T_NEW","context":"normal","value":"new","line":6,"start":80,"end":83},{"type":"T_IDENTIFIER","context":"normal","value":"Number","line":6,"start":84,"end":90},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":90,"end":91},{"type":"T_NUMBER","context":"normal","value":"42","line":6,"start":91,"end":93},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":93,"end":94},{"type":"T_COMMA","context":"normal","value":",","line":6,"start":94,"end":95},{"type":"T_NEW","context":"normal","value":"new","line":6,"start":96,"end":99},{"type":"T_IDENTIFIER","context":"normal","value":"String","line":6,"start":100,"end":106},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":106,"end":107},{"type":"T_STRING","context":"normal","value":"\"world\"","line":6,"start":107,"end":114},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":114,"end":115},{"type":"T_COMMA","context":"normal","value":",","line":6,"start":115,"end":116},{"type":"T_NEW","context":"normal","value":"new","line":6,"start":117,"end":120},{"type":"T_IDENTIFIER","context":"normal","value":"Boolean","line":6,"start":121,"end":128},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":128,"end":129},{"type":"T_FALSE","context":"normal","value":"false","line":6,"start":129,"end":134},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":134,"end":135},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":135,"end":136},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":136,"end":137}],"errors":[]}
These wrapper objects are rarely used.
Booleans
Booleans are true
and false
values in JavaScript. The boolean
type in
Flow accepts these values.
1
2
3
4
5
6
7
8
|
function acceptsBoolean(value: boolean) {
}
acceptsBoolean(true);
acceptsBoolean(false);
acceptsBoolean("foo");
|
Cannot call `acceptsBoolean` with `"foo"` bound to `value` because string [1] is incompatible with boolean [2]. [incompatible-call]
{"value":"// @flow\nfunction acceptsBoolean(value: boolean) {\n // ...\n}\n\nacceptsBoolean(true); // Works!\nacceptsBoolean(false); // Works!\nacceptsBoolean(\"foo\"); // Error!\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":"acceptsBoolean","line":2,"start":18,"end":32},{"type":"T_LPAREN","context":"normal","value":"(","line":2,"start":32,"end":33},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":2,"start":33,"end":38},{"type":"T_COLON","context":"type","value":":","line":2,"start":38,"end":39},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":2,"start":40,"end":47},{"type":"T_RPAREN","context":"normal","value":")","line":2,"start":47,"end":48},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":49,"end":50},{"type":"Line","context":"comment","value":"// ...","line":3,"start":53,"end":59},{"type":"T_RCURLY","context":"normal","value":"}","line":4,"start":60,"end":61},{"type":"T_IDENTIFIER","context":"normal","value":"acceptsBoolean","line":6,"start":63,"end":77},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":77,"end":78},{"type":"T_TRUE","context":"normal","value":"true","line":6,"start":78,"end":82},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":82,"end":83},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":83,"end":84},{"type":"Line","context":"comment","value":"// Works!","line":6,"start":86,"end":95},{"type":"T_IDENTIFIER","context":"normal","value":"acceptsBoolean","line":7,"start":96,"end":110},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":110,"end":111},{"type":"T_FALSE","context":"normal","value":"false","line":7,"start":111,"end":116},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":116,"end":117},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":117,"end":118},{"type":"Line","context":"comment","value":"// Works!","line":7,"start":119,"end":128},{"type":"T_IDENTIFIER","context":"normal","value":"acceptsBoolean","line":8,"start":129,"end":143},{"type":"T_LPAREN","context":"normal","value":"(","line":8,"start":143,"end":144},{"type":"T_STRING","context":"normal","value":"\"foo\"","line":8,"start":144,"end":149},{"type":"T_RPAREN","context":"normal","value":")","line":8,"start":149,"end":150},{"type":"T_SEMICOLON","context":"normal","value":";","line":8,"start":150,"end":151},{"type":"Line","context":"comment","value":"// Error!","line":8,"start":152,"end":161}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot call `acceptsBoolean` with `\"foo\"` bound to `value` because string [1] is incompatible with boolean [2]. [incompatible-call]","context":"acceptsBoolean(\"foo\"); // Error!","source":"-","start":{"line":8,"column":16,"offset":144},"end":{"line":8,"column":20,"offset":149}}],"operation":null}]}
JavaScript can also implicitly convert other types of values into booleans.
1
2
|
if (42) {}
if ("") {}
|
{"value":"if (42) {} // 42 => true\nif (\"\") {} // \"\" => false\n","tokens":[{"type":"T_IF","context":"normal","value":"if","line":1,"start":0,"end":2},{"type":"T_LPAREN","context":"normal","value":"(","line":1,"start":3,"end":4},{"type":"T_NUMBER","context":"normal","value":"42","line":1,"start":4,"end":6},{"type":"T_RPAREN","context":"normal","value":")","line":1,"start":6,"end":7},{"type":"T_LCURLY","context":"normal","value":"{","line":1,"start":8,"end":9},{"type":"T_RCURLY","context":"normal","value":"}","line":1,"start":9,"end":10},{"type":"Line","context":"comment","value":"// 42 => true","line":1,"start":11,"end":24},{"type":"T_IF","context":"normal","value":"if","line":2,"start":25,"end":27},{"type":"T_LPAREN","context":"normal","value":"(","line":2,"start":28,"end":29},{"type":"T_STRING","context":"normal","value":"\"\"","line":2,"start":29,"end":31},{"type":"T_RPAREN","context":"normal","value":")","line":2,"start":31,"end":32},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":33,"end":34},{"type":"T_RCURLY","context":"normal","value":"}","line":2,"start":34,"end":35},{"type":"Line","context":"comment","value":"// \"\" => false","line":2,"start":36,"end":50}],"errors":[]}
Flow understands these conversions and will allow any of them as part of an
if
statement and other types of expressions.
Boolean types need you to be explicit by converting non-booleans. You can do
that with Boolean(x)
or !!x
.
1
2
3
4
5
6
7
8
|
function acceptsBoolean(value: boolean) {
}
acceptsBoolean(0);
acceptsBoolean(Boolean(0));
acceptsBoolean(!!0);
|
Cannot call `acceptsBoolean` with `0` bound to `value` because number [1] is incompatible with boolean [2]. [incompatible-call]
{"value":"// @flow\nfunction acceptsBoolean(value: boolean) {\n // ...\n}\n\nacceptsBoolean(0); // Error!\nacceptsBoolean(Boolean(0)); // Works!\nacceptsBoolean(!!0); // 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":"acceptsBoolean","line":2,"start":18,"end":32},{"type":"T_LPAREN","context":"normal","value":"(","line":2,"start":32,"end":33},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":2,"start":33,"end":38},{"type":"T_COLON","context":"type","value":":","line":2,"start":38,"end":39},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":2,"start":40,"end":47},{"type":"T_RPAREN","context":"normal","value":")","line":2,"start":47,"end":48},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":49,"end":50},{"type":"Line","context":"comment","value":"// ...","line":3,"start":53,"end":59},{"type":"T_RCURLY","context":"normal","value":"}","line":4,"start":60,"end":61},{"type":"T_IDENTIFIER","context":"normal","value":"acceptsBoolean","line":6,"start":63,"end":77},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":77,"end":78},{"type":"T_NUMBER","context":"normal","value":"0","line":6,"start":78,"end":79},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":79,"end":80},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":80,"end":81},{"type":"Line","context":"comment","value":"// Error!","line":6,"start":91,"end":100},{"type":"T_IDENTIFIER","context":"normal","value":"acceptsBoolean","line":7,"start":101,"end":115},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":115,"end":116},{"type":"T_IDENTIFIER","context":"normal","value":"Boolean","line":7,"start":116,"end":123},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":123,"end":124},{"type":"T_NUMBER","context":"normal","value":"0","line":7,"start":124,"end":125},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":125,"end":126},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":126,"end":127},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":127,"end":128},{"type":"Line","context":"comment","value":"// Works!","line":7,"start":129,"end":138},{"type":"T_IDENTIFIER","context":"normal","value":"acceptsBoolean","line":8,"start":139,"end":153},{"type":"T_LPAREN","context":"normal","value":"(","line":8,"start":153,"end":154},{"type":"T_NOT","context":"normal","value":"!","line":8,"start":154,"end":155},{"type":"T_NOT","context":"normal","value":"!","line":8,"start":155,"end":156},{"type":"T_NUMBER","context":"normal","value":"0","line":8,"start":156,"end":157},{"type":"T_RPAREN","context":"normal","value":")","line":8,"start":157,"end":158},{"type":"T_SEMICOLON","context":"normal","value":";","line":8,"start":158,"end":159},{"type":"Line","context":"comment","value":"// Works!","line":8,"start":167,"end":176}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot call `acceptsBoolean` with `0` bound to `value` because number [1] is incompatible with boolean [2]. [incompatible-call]","context":"acceptsBoolean(0); // Error!","source":"-","start":{"line":6,"column":16,"offset":78},"end":{"line":6,"column":16,"offset":79}}],"operation":null}]}
Remember that boolean
and Boolean
are different types.
- A
boolean
is a literal value like true
or false
or the result of an
expression like a === b
.
- A
Boolean
is a wrapper object created by the global new Boolean(x)
constructor.
Numbers
Unlike many other languages, JavaScript only has one type of number. These
values may appear as 42
or 3.14
. JavaScript also considers Infinity
and
NaN
to be numbers. The number
type captures everything JavaScript considers
a number.
1
2
3
4
5
6
7
8
9
10
|
function acceptsNumber(value: number) {
}
acceptsNumber(42);
acceptsNumber(3.14);
acceptsNumber(NaN);
acceptsNumber(Infinity);
acceptsNumber("foo");
|
Cannot call `acceptsNumber` with `"foo"` bound to `value` because string [1] is incompatible with number [2]. [incompatible-call]
{"value":"// @flow\nfunction acceptsNumber(value: number) {\n // ...\n}\n\nacceptsNumber(42); // Works!\nacceptsNumber(3.14); // Works!\nacceptsNumber(NaN); // Works!\nacceptsNumber(Infinity); // Works!\nacceptsNumber(\"foo\"); // Error!\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":"acceptsNumber","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_NUMBER_TYPE","context":"type","value":"number","line":2,"start":39,"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":"acceptsNumber","line":6,"start":61,"end":74},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":74,"end":75},{"type":"T_NUMBER","context":"normal","value":"42","line":6,"start":75,"end":77},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":77,"end":78},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":78,"end":79},{"type":"Line","context":"comment","value":"// Works!","line":6,"start":86,"end":95},{"type":"T_IDENTIFIER","context":"normal","value":"acceptsNumber","line":7,"start":96,"end":109},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":109,"end":110},{"type":"T_NUMBER","context":"normal","value":"3.14","line":7,"start":110,"end":114},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":114,"end":115},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":115,"end":116},{"type":"Line","context":"comment","value":"// Works!","line":7,"start":121,"end":130},{"type":"T_IDENTIFIER","context":"normal","value":"acceptsNumber","line":8,"start":131,"end":144},{"type":"T_LPAREN","context":"normal","value":"(","line":8,"start":144,"end":145},{"type":"T_IDENTIFIER","context":"normal","value":"NaN","line":8,"start":145,"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":"Line","context":"comment","value":"// Works!","line":8,"start":156,"end":165},{"type":"T_IDENTIFIER","context":"normal","value":"acceptsNumber","line":9,"start":166,"end":179},{"type":"T_LPAREN","context":"normal","value":"(","line":9,"start":179,"end":180},{"type":"T_IDENTIFIER","context":"normal","value":"Infinity","line":9,"start":180,"end":188},{"type":"T_RPAREN","context":"normal","value":")","line":9,"start":188,"end":189},{"type":"T_SEMICOLON","context":"normal","value":";","line":9,"start":189,"end":190},{"type":"Line","context":"comment","value":"// Works!","line":9,"start":191,"end":200},{"type":"T_IDENTIFIER","context":"normal","value":"acceptsNumber","line":10,"start":201,"end":214},{"type":"T_LPAREN","context":"normal","value":"(","line":10,"start":214,"end":215},{"type":"T_STRING","context":"normal","value":"\"foo\"","line":10,"start":215,"end":220},{"type":"T_RPAREN","context":"normal","value":")","line":10,"start":220,"end":221},{"type":"T_SEMICOLON","context":"normal","value":";","line":10,"start":221,"end":222},{"type":"Line","context":"comment","value":"// Error!","line":10,"start":226,"end":235}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot call `acceptsNumber` with `\"foo\"` bound to `value` because string [1] is incompatible with number [2]. [incompatible-call]","context":"acceptsNumber(\"foo\"); // Error!","source":"-","start":{"line":10,"column":15,"offset":215},"end":{"line":10,"column":19,"offset":220}}],"operation":null}]}
Remember that number
and Number
are different types.
- A
number
is a literal value like 42
or 3.14
or the result of an
expression like parseFloat(x)
.
- A
Number
is a wrapper object created by the global new Number(x)
constructor.
Strings
Strings are "foo"
values in JavaScript. The string
type in Flow accepts
these values.
1
2
3
4
5
6
7
|
function acceptsString(value: string) {
}
acceptsString("foo");
acceptsString(false);
|
Cannot call `acceptsString` with `false` bound to `value` because boolean [1] is incompatible with string [2]. [incompatible-call]
{"value":"// @flow\nfunction acceptsString(value: string) {\n // ...\n}\n\nacceptsString(\"foo\"); // Works!\nacceptsString(false); // Error!\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":"acceptsString","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_STRING_TYPE","context":"type","value":"string","line":2,"start":39,"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":"acceptsString","line":6,"start":61,"end":74},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":74,"end":75},{"type":"T_STRING","context":"normal","value":"\"foo\"","line":6,"start":75,"end":80},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":80,"end":81},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":81,"end":82},{"type":"Line","context":"comment","value":"// Works!","line":6,"start":83,"end":92},{"type":"T_IDENTIFIER","context":"normal","value":"acceptsString","line":7,"start":93,"end":106},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":106,"end":107},{"type":"T_FALSE","context":"normal","value":"false","line":7,"start":107,"end":112},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":112,"end":113},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":113,"end":114},{"type":"Line","context":"comment","value":"// Error!","line":7,"start":115,"end":124}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot call `acceptsString` with `false` bound to `value` because boolean [1] is incompatible with string [2]. [incompatible-call]","context":"acceptsString(false); // Error!","source":"-","start":{"line":7,"column":15,"offset":107},"end":{"line":7,"column":19,"offset":112}}],"operation":null}]}
JavaScript implicitly converts other types of values into strings by
concatenating them.
1
2
|
"foo" + 42;
"foo" + {};
|
{"value":"\"foo\" + 42; // \"foo42\"\n\"foo\" + {}; // \"foo[object Object]\"\n","tokens":[{"type":"T_STRING","context":"normal","value":"\"foo\"","line":1,"start":0,"end":5},{"type":"T_PLUS","context":"normal","value":"+","line":1,"start":6,"end":7},{"type":"T_NUMBER","context":"normal","value":"42","line":1,"start":8,"end":10},{"type":"T_SEMICOLON","context":"normal","value":";","line":1,"start":10,"end":11},{"type":"Line","context":"comment","value":"// \"foo42\"","line":1,"start":12,"end":22},{"type":"T_STRING","context":"normal","value":"\"foo\"","line":2,"start":23,"end":28},{"type":"T_PLUS","context":"normal","value":"+","line":2,"start":29,"end":30},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":31,"end":32},{"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":"// \"foo[object Object]\"","line":2,"start":35,"end":58}],"errors":[]}
Flow will only accept strings and number when concatenating them to strings.
1
2
3
4
5
|
"foo" + "foo";
"foo" + 42;
"foo" + {};
"foo" + [];
|
Cannot add `"foo"` and object literal because object literal [1] is incompatible with string [2]. [incompatible-type]
Cannot add `"foo"` and array literal because empty array literal [1] is incompatible with string [2]. [incompatible-type]
{"value":"// @flow\n\"foo\" + \"foo\"; // Works!\n\"foo\" + 42; // Works!\n\"foo\" + {}; // Error!\n\"foo\" + []; // Error!\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_STRING","context":"normal","value":"\"foo\"","line":2,"start":9,"end":14},{"type":"T_PLUS","context":"normal","value":"+","line":2,"start":15,"end":16},{"type":"T_STRING","context":"normal","value":"\"foo\"","line":2,"start":17,"end":22},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":22,"end":23},{"type":"Line","context":"comment","value":"// Works!","line":2,"start":24,"end":33},{"type":"T_STRING","context":"normal","value":"\"foo\"","line":3,"start":34,"end":39},{"type":"T_PLUS","context":"normal","value":"+","line":3,"start":40,"end":41},{"type":"T_NUMBER","context":"normal","value":"42","line":3,"start":42,"end":44},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":44,"end":45},{"type":"Line","context":"comment","value":"// Works!","line":3,"start":49,"end":58},{"type":"T_STRING","context":"normal","value":"\"foo\"","line":4,"start":59,"end":64},{"type":"T_PLUS","context":"normal","value":"+","line":4,"start":65,"end":66},{"type":"T_LCURLY","context":"normal","value":"{","line":4,"start":67,"end":68},{"type":"T_RCURLY","context":"normal","value":"}","line":4,"start":68,"end":69},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":69,"end":70},{"type":"Line","context":"comment","value":"// Error!","line":4,"start":74,"end":83},{"type":"T_STRING","context":"normal","value":"\"foo\"","line":5,"start":84,"end":89},{"type":"T_PLUS","context":"normal","value":"+","line":5,"start":90,"end":91},{"type":"T_LBRACKET","context":"normal","value":"[","line":5,"start":92,"end":93},{"type":"T_RBRACKET","context":"normal","value":"]","line":5,"start":93,"end":94},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":94,"end":95},{"type":"Line","context":"comment","value":"// Error!","line":5,"start":99,"end":108}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot add `\"foo\"` and object literal because object literal [1] is incompatible with string [2]. [incompatible-type]","context":"\"foo\" + {}; // Error!","source":"-","start":{"line":4,"column":9,"offset":67},"end":{"line":4,"column":10,"offset":69}}],"operation":null},{"id":"E2","messages":[{"id":"E2M1","description":"Cannot add `\"foo\"` and array literal because empty array literal [1] is incompatible with string [2]. [incompatible-type]","context":"\"foo\" + []; // Error!","source":"-","start":{"line":5,"column":9,"offset":92},"end":{"line":5,"column":10,"offset":94}}],"operation":null}]}
You must be explicit and convert other types into strings. You can do this by
using the String method or using another method for stringifying values.
1
2
3
4
|
"foo" + String({});
"foo" + [].toString();
"" + JSON.stringify({})
|
{"value":"// @flow\n\"foo\" + String({}); // Works!\n\"foo\" + [].toString(); // Works!\n\"\" + JSON.stringify({}) // Works!\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_STRING","context":"normal","value":"\"foo\"","line":2,"start":9,"end":14},{"type":"T_PLUS","context":"normal","value":"+","line":2,"start":15,"end":16},{"type":"T_IDENTIFIER","context":"normal","value":"String","line":2,"start":17,"end":23},{"type":"T_LPAREN","context":"normal","value":"(","line":2,"start":23,"end":24},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":24,"end":25},{"type":"T_RCURLY","context":"normal","value":"}","line":2,"start":25,"end":26},{"type":"T_RPAREN","context":"normal","value":")","line":2,"start":26,"end":27},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":27,"end":28},{"type":"Line","context":"comment","value":"// Works!","line":2,"start":33,"end":42},{"type":"T_STRING","context":"normal","value":"\"foo\"","line":3,"start":43,"end":48},{"type":"T_PLUS","context":"normal","value":"+","line":3,"start":49,"end":50},{"type":"T_LBRACKET","context":"normal","value":"[","line":3,"start":51,"end":52},{"type":"T_RBRACKET","context":"normal","value":"]","line":3,"start":52,"end":53},{"type":"T_PERIOD","context":"normal","value":".","line":3,"start":53,"end":54},{"type":"T_IDENTIFIER","context":"normal","value":"toString","line":3,"start":54,"end":62},{"type":"T_LPAREN","context":"normal","value":"(","line":3,"start":62,"end":63},{"type":"T_RPAREN","context":"normal","value":")","line":3,"start":63,"end":64},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":64,"end":65},{"type":"Line","context":"comment","value":"// Works!","line":3,"start":67,"end":76},{"type":"T_STRING","context":"normal","value":"\"\"","line":4,"start":77,"end":79},{"type":"T_PLUS","context":"normal","value":"+","line":4,"start":80,"end":81},{"type":"T_IDENTIFIER","context":"normal","value":"JSON","line":4,"start":82,"end":86},{"type":"T_PERIOD","context":"normal","value":".","line":4,"start":86,"end":87},{"type":"T_IDENTIFIER","context":"normal","value":"stringify","line":4,"start":87,"end":96},{"type":"T_LPAREN","context":"normal","value":"(","line":4,"start":96,"end":97},{"type":"T_LCURLY","context":"normal","value":"{","line":4,"start":97,"end":98},{"type":"T_RCURLY","context":"normal","value":"}","line":4,"start":98,"end":99},{"type":"T_RPAREN","context":"normal","value":")","line":4,"start":99,"end":100},{"type":"Line","context":"comment","value":"// Works!","line":4,"start":101,"end":110}],"errors":[]}
Remember that string
and String
are different types.
- A
string
is a literal value like "foo"
or the result of an expression
like "" + 42
.
- A
String
is a wrapper object created by the global new String(x)
constructor.
null
and void
JavaScript has both null
and undefined
. Flow treats these as separate
types: null
and void
(for undefined
).
1
2
3
4
5
6
7
8
9
10
11
12
13
|
function acceptsNull(value: null) {
}
function acceptsUndefined(value: void) {
}
acceptsNull(null);
acceptsNull(undefined);
acceptsUndefined(null);
acceptsUndefined(undefined);
|
Cannot call `acceptsNull` with `undefined` bound to `value` because undefined [1] is incompatible with null [2]. [incompatible-call]
Cannot call `acceptsUndefined` with `null` bound to `value` because null [1] is incompatible with undefined [2]. [incompatible-call]
{"value":"// @flow\nfunction acceptsNull(value: null) {\n /* ... */\n}\n\nfunction acceptsUndefined(value: void) {\n /* ... */\n}\n\nacceptsNull(null); // Works!\nacceptsNull(undefined); // Error!\nacceptsUndefined(null); // Error!\nacceptsUndefined(undefined); // 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":"acceptsNull","line":2,"start":18,"end":29},{"type":"T_LPAREN","context":"normal","value":"(","line":2,"start":29,"end":30},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":2,"start":30,"end":35},{"type":"T_COLON","context":"type","value":":","line":2,"start":35,"end":36},{"type":"T_NULL","context":"type","value":"null","line":2,"start":37,"end":41},{"type":"T_RPAREN","context":"normal","value":")","line":2,"start":41,"end":42},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":43,"end":44},{"type":"Block","context":"comment","value":"/* ... */","line":3,"start":47,"end":56},{"type":"T_RCURLY","context":"normal","value":"}","line":4,"start":57,"end":58},{"type":"T_FUNCTION","context":"normal","value":"function","line":6,"start":60,"end":68},{"type":"T_IDENTIFIER","context":"normal","value":"acceptsUndefined","line":6,"start":69,"end":85},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":85,"end":86},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":6,"start":86,"end":91},{"type":"T_COLON","context":"type","value":":","line":6,"start":91,"end":92},{"type":"T_VOID_TYPE","context":"type","value":"void","line":6,"start":93,"end":97},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":97,"end":98},{"type":"T_LCURLY","context":"normal","value":"{","line":6,"start":99,"end":100},{"type":"Block","context":"comment","value":"/* ... */","line":7,"start":103,"end":112},{"type":"T_RCURLY","context":"normal","value":"}","line":8,"start":113,"end":114},{"type":"T_IDENTIFIER","context":"normal","value":"acceptsNull","line":10,"start":116,"end":127},{"type":"T_LPAREN","context":"normal","value":"(","line":10,"start":127,"end":128},{"type":"T_NULL","context":"normal","value":"null","line":10,"start":128,"end":132},{"type":"T_RPAREN","context":"normal","value":")","line":10,"start":132,"end":133},{"type":"T_SEMICOLON","context":"normal","value":";","line":10,"start":133,"end":134},{"type":"Line","context":"comment","value":"// Works!","line":10,"start":140,"end":149},{"type":"T_IDENTIFIER","context":"normal","value":"acceptsNull","line":11,"start":150,"end":161},{"type":"T_LPAREN","context":"normal","value":"(","line":11,"start":161,"end":162},{"type":"T_IDENTIFIER","context":"normal","value":"undefined","line":11,"start":162,"end":171},{"type":"T_RPAREN","context":"normal","value":")","line":11,"start":171,"end":172},{"type":"T_SEMICOLON","context":"normal","value":";","line":11,"start":172,"end":173},{"type":"Line","context":"comment","value":"// Error!","line":11,"start":174,"end":183},{"type":"T_IDENTIFIER","context":"normal","value":"acceptsUndefined","line":12,"start":184,"end":200},{"type":"T_LPAREN","context":"normal","value":"(","line":12,"start":200,"end":201},{"type":"T_NULL","context":"normal","value":"null","line":12,"start":201,"end":205},{"type":"T_RPAREN","context":"normal","value":")","line":12,"start":205,"end":206},{"type":"T_SEMICOLON","context":"normal","value":";","line":12,"start":206,"end":207},{"type":"Line","context":"comment","value":"// Error!","line":12,"start":213,"end":222},{"type":"T_IDENTIFIER","context":"normal","value":"acceptsUndefined","line":13,"start":223,"end":239},{"type":"T_LPAREN","context":"normal","value":"(","line":13,"start":239,"end":240},{"type":"T_IDENTIFIER","context":"normal","value":"undefined","line":13,"start":240,"end":249},{"type":"T_RPAREN","context":"normal","value":")","line":13,"start":249,"end":250},{"type":"T_SEMICOLON","context":"normal","value":";","line":13,"start":250,"end":251},{"type":"Line","context":"comment","value":"// Works!","line":13,"start":252,"end":261}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot call `acceptsNull` with `undefined` bound to `value` because undefined [1] is incompatible with null [2]. [incompatible-call]","context":"acceptsNull(undefined); // Error!","source":"-","start":{"line":11,"column":13,"offset":162},"end":{"line":11,"column":21,"offset":171}}],"operation":null},{"id":"E2","messages":[{"id":"E2M1","description":"Cannot call `acceptsUndefined` with `null` bound to `value` because null [1] is incompatible with undefined [2]. [incompatible-call]","context":"acceptsUndefined(null); // Error!","source":"-","start":{"line":12,"column":18,"offset":201},"end":{"line":12,"column":21,"offset":205}}],"operation":null}]}
null
and void
also appear in other types.
Maybe types
Maybe types are for places where a value is optional and you can create them by
adding a question mark in front of the type such as ?string
or ?number
.
In addition to the type
in ?type
, maybe types can also be null
or void
.
1
2
3
4
5
6
7
8
9
|
function acceptsMaybeString(value: ?string) {
}
acceptsMaybeString("bar");
acceptsMaybeString(undefined);
acceptsMaybeString(null);
acceptsMaybeString();
|
{"value":"// @flow\nfunction acceptsMaybeString(value: ?string) {\n // ...\n}\n\nacceptsMaybeString(\"bar\"); // Works!\nacceptsMaybeString(undefined); // Works!\nacceptsMaybeString(null); // Works!\nacceptsMaybeString(); // 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":"acceptsMaybeString","line":2,"start":18,"end":36},{"type":"T_LPAREN","context":"normal","value":"(","line":2,"start":36,"end":37},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":2,"start":37,"end":42},{"type":"T_COLON","context":"type","value":":","line":2,"start":42,"end":43},{"type":"T_PLING","context":"type","value":"?","line":2,"start":44,"end":45},{"type":"T_STRING_TYPE","context":"type","value":"string","line":2,"start":45,"end":51},{"type":"T_RPAREN","context":"normal","value":")","line":2,"start":51,"end":52},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":53,"end":54},{"type":"Line","context":"comment","value":"// ...","line":3,"start":57,"end":63},{"type":"T_RCURLY","context":"normal","value":"}","line":4,"start":64,"end":65},{"type":"T_IDENTIFIER","context":"normal","value":"acceptsMaybeString","line":6,"start":67,"end":85},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":85,"end":86},{"type":"T_STRING","context":"normal","value":"\"bar\"","line":6,"start":86,"end":91},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":91,"end":92},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":92,"end":93},{"type":"Line","context":"comment","value":"// Works!","line":6,"start":98,"end":107},{"type":"T_IDENTIFIER","context":"normal","value":"acceptsMaybeString","line":7,"start":108,"end":126},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":126,"end":127},{"type":"T_IDENTIFIER","context":"normal","value":"undefined","line":7,"start":127,"end":136},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":136,"end":137},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":137,"end":138},{"type":"Line","context":"comment","value":"// Works!","line":7,"start":139,"end":148},{"type":"T_IDENTIFIER","context":"normal","value":"acceptsMaybeString","line":8,"start":149,"end":167},{"type":"T_LPAREN","context":"normal","value":"(","line":8,"start":167,"end":168},{"type":"T_NULL","context":"normal","value":"null","line":8,"start":168,"end":172},{"type":"T_RPAREN","context":"normal","value":")","line":8,"start":172,"end":173},{"type":"T_SEMICOLON","context":"normal","value":";","line":8,"start":173,"end":174},{"type":"Line","context":"comment","value":"// Works!","line":8,"start":180,"end":189},{"type":"T_IDENTIFIER","context":"normal","value":"acceptsMaybeString","line":9,"start":190,"end":208},{"type":"T_LPAREN","context":"normal","value":"(","line":9,"start":208,"end":209},{"type":"T_RPAREN","context":"normal","value":")","line":9,"start":209,"end":210},{"type":"T_SEMICOLON","context":"normal","value":";","line":9,"start":210,"end":211},{"type":"Line","context":"comment","value":"// Works!","line":9,"start":221,"end":230}],"errors":[]}
Optional object properties
Object types can have optional properties where a question mark ?
comes after
the property name.
1
|
{ propertyName?: string }
|
{"value":"{ propertyName?: string }\n","tokens":[{"type":"T_LCURLY","context":"normal","value":"{","line":1,"start":0,"end":1},{"type":"T_IDENTIFIER","context":"normal","value":"propertyName","line":1,"start":2,"end":14},{"type":"T_PLING","context":"normal","value":"?","line":1,"start":14,"end":15},{"type":"T_COLON","context":"normal","value":":","line":1,"start":15,"end":16},{"type":"T_IDENTIFIER","context":"normal","value":"string","line":1,"start":17,"end":23},{"type":"T_RCURLY","context":"normal","value":"}","line":1,"start":24,"end":25}],"errors":[]}
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
|
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!\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":"T_IDENTIFIER","context":"normal","value":"acceptsObject","line":8,"start":161,"end":174},{"type":"T_LPAREN","context":"normal","value":"(","line":8,"start":174,"end":175},{"type":"T_LCURLY","context":"normal","value":"{","line":8,"start":175,"end":176},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":8,"start":177,"end":180},{"type":"T_COLON","context":"normal","value":":","line":8,"start":180,"end":181},{"type":"T_NULL","context":"normal","value":"null","line":8,"start":182,"end":186},{"type":"T_RCURLY","context":"normal","value":"}","line":8,"start":187,"end":188},{"type":"T_RPAREN","context":"normal","value":")","line":8,"start":188,"end":189},{"type":"T_SEMICOLON","context":"normal","value":";","line":8,"start":189,"end":190},{"type":"Line","context":"comment","value":"// Error!","line":8,"start":196,"end":205},{"type":"T_IDENTIFIER","context":"normal","value":"acceptsObject","line":9,"start":206,"end":219},{"type":"T_LPAREN","context":"normal","value":"(","line":9,"start":219,"end":220},{"type":"T_LCURLY","context":"normal","value":"{","line":9,"start":220,"end":221},{"type":"T_RCURLY","context":"normal","value":"}","line":9,"start":221,"end":222},{"type":"T_RPAREN","context":"normal","value":")","line":9,"start":222,"end":223},{"type":"T_SEMICOLON","context":"normal","value":";","line":9,"start":223,"end":224},{"type":"Line","context":"comment","value":"// Works!","line":9,"start":241,"end":250}],"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":8,"column":22,"offset":182},"end":{"line":8,"column":25,"offset":186}}],"operation":null}]}
Optional function parameters
Functions can have optional parameters where a question mark ?
comes after
the parameter name.
1
|
function method(param?: string) { }
|
{"value":"function method(param?: string) { /* ... */ }\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":"param","line":1,"start":16,"end":21},{"type":"T_PLING","context":"normal","value":"?","line":1,"start":21,"end":22},{"type":"T_COLON","context":"type","value":":","line":1,"start":22,"end":23},{"type":"T_STRING_TYPE","context":"type","value":"string","line":1,"start":24,"end":30},{"type":"T_RPAREN","context":"normal","value":")","line":1,"start":30,"end":31},{"type":"T_LCURLY","context":"normal","value":"{","line":1,"start":32,"end":33},{"type":"Block","context":"comment","value":"/* ... */","line":1,"start":34,"end":43},{"type":"T_RCURLY","context":"normal","value":"}","line":1,"start":44,"end":45}],"errors":[]}
In addition to their set type, these optional parameters can either be void
or omitted altogether. However, they cannot be null
.
1
2
3
4
5
6
7
8
9
|
function acceptsOptionalString(value?: string) {
}
acceptsOptionalString("bar");
acceptsOptionalString(undefined);
acceptsOptionalString(null);
acceptsOptionalString();
|
Cannot call `acceptsOptionalString` with `null` bound to `value` because null [1] is incompatible with string [2]. [incompatible-call]
{"value":"// @flow\nfunction acceptsOptionalString(value?: string) {\n // ...\n}\n\nacceptsOptionalString(\"bar\"); // Works!\nacceptsOptionalString(undefined); // Works!\nacceptsOptionalString(null); // Error!\nacceptsOptionalString(); // 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":"acceptsOptionalString","line":2,"start":18,"end":39},{"type":"T_LPAREN","context":"normal","value":"(","line":2,"start":39,"end":40},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":2,"start":40,"end":45},{"type":"T_PLING","context":"normal","value":"?","line":2,"start":45,"end":46},{"type":"T_COLON","context":"type","value":":","line":2,"start":46,"end":47},{"type":"T_STRING_TYPE","context":"type","value":"string","line":2,"start":48,"end":54},{"type":"T_RPAREN","context":"normal","value":")","line":2,"start":54,"end":55},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":56,"end":57},{"type":"Line","context":"comment","value":"// ...","line":3,"start":60,"end":66},{"type":"T_RCURLY","context":"normal","value":"}","line":4,"start":67,"end":68},{"type":"T_IDENTIFIER","context":"normal","value":"acceptsOptionalString","line":6,"start":70,"end":91},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":91,"end":92},{"type":"T_STRING","context":"normal","value":"\"bar\"","line":6,"start":92,"end":97},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":97,"end":98},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":98,"end":99},{"type":"Line","context":"comment","value":"// Works!","line":6,"start":104,"end":113},{"type":"T_IDENTIFIER","context":"normal","value":"acceptsOptionalString","line":7,"start":114,"end":135},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":135,"end":136},{"type":"T_IDENTIFIER","context":"normal","value":"undefined","line":7,"start":136,"end":145},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":145,"end":146},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":146,"end":147},{"type":"Line","context":"comment","value":"// Works!","line":7,"start":148,"end":157},{"type":"T_IDENTIFIER","context":"normal","value":"acceptsOptionalString","line":8,"start":158,"end":179},{"type":"T_LPAREN","context":"normal","value":"(","line":8,"start":179,"end":180},{"type":"T_NULL","context":"normal","value":"null","line":8,"start":180,"end":184},{"type":"T_RPAREN","context":"normal","value":")","line":8,"start":184,"end":185},{"type":"T_SEMICOLON","context":"normal","value":";","line":8,"start":185,"end":186},{"type":"Line","context":"comment","value":"// Error!","line":8,"start":192,"end":201},{"type":"T_IDENTIFIER","context":"normal","value":"acceptsOptionalString","line":9,"start":202,"end":223},{"type":"T_LPAREN","context":"normal","value":"(","line":9,"start":223,"end":224},{"type":"T_RPAREN","context":"normal","value":")","line":9,"start":224,"end":225},{"type":"T_SEMICOLON","context":"normal","value":";","line":9,"start":225,"end":226},{"type":"Line","context":"comment","value":"// Works!","line":9,"start":236,"end":245}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot call `acceptsOptionalString` with `null` bound to `value` because null [1] is incompatible with string [2]. [incompatible-call]","context":"acceptsOptionalString(null); // Error!","source":"-","start":{"line":8,"column":23,"offset":180},"end":{"line":8,"column":26,"offset":184}}],"operation":null}]}
Function parameters with defaults
Function parameters can also have defaults. This is a feature of ECMAScript
2015.
1
|
function method(value: string = "default") { }
|
{"value":"function method(value: string = \"default\") { /* ... */ }\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":"value","line":1,"start":16,"end":21},{"type":"T_COLON","context":"type","value":":","line":1,"start":21,"end":22},{"type":"T_STRING_TYPE","context":"type","value":"string","line":1,"start":23,"end":29},{"type":"T_ASSIGN","context":"normal","value":"=","line":1,"start":30,"end":31},{"type":"T_STRING","context":"normal","value":"\"default\"","line":1,"start":32,"end":41},{"type":"T_RPAREN","context":"normal","value":")","line":1,"start":41,"end":42},{"type":"T_LCURLY","context":"normal","value":"{","line":1,"start":43,"end":44},{"type":"Block","context":"comment","value":"/* ... */","line":1,"start":45,"end":54},{"type":"T_RCURLY","context":"normal","value":"}","line":1,"start":55,"end":56}],"errors":[]}
In addition to their set type, default parameters can also be void
or omitted
altogether. However, they cannot be null
.
1
2
3
4
5
6
7
8
9
|
function acceptsOptionalString(value: string = "foo") {
}
acceptsOptionalString("bar");
acceptsOptionalString(undefined);
acceptsOptionalString(null);
acceptsOptionalString();
|
Cannot call `acceptsOptionalString` with `null` bound to `value` because null [1] is incompatible with string [2]. [incompatible-call]
{"value":"// @flow\nfunction acceptsOptionalString(value: string = \"foo\") {\n // ...\n}\n\nacceptsOptionalString(\"bar\"); // Works!\nacceptsOptionalString(undefined); // Works!\nacceptsOptionalString(null); // Error!\nacceptsOptionalString(); // 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":"acceptsOptionalString","line":2,"start":18,"end":39},{"type":"T_LPAREN","context":"normal","value":"(","line":2,"start":39,"end":40},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":2,"start":40,"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_ASSIGN","context":"normal","value":"=","line":2,"start":54,"end":55},{"type":"T_STRING","context":"normal","value":"\"foo\"","line":2,"start":56,"end":61},{"type":"T_RPAREN","context":"normal","value":")","line":2,"start":61,"end":62},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":63,"end":64},{"type":"Line","context":"comment","value":"// ...","line":3,"start":67,"end":73},{"type":"T_RCURLY","context":"normal","value":"}","line":4,"start":74,"end":75},{"type":"T_IDENTIFIER","context":"normal","value":"acceptsOptionalString","line":6,"start":77,"end":98},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":98,"end":99},{"type":"T_STRING","context":"normal","value":"\"bar\"","line":6,"start":99,"end":104},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":104,"end":105},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":105,"end":106},{"type":"Line","context":"comment","value":"// Works!","line":6,"start":111,"end":120},{"type":"T_IDENTIFIER","context":"normal","value":"acceptsOptionalString","line":7,"start":121,"end":142},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":142,"end":143},{"type":"T_IDENTIFIER","context":"normal","value":"undefined","line":7,"start":143,"end":152},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":152,"end":153},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":153,"end":154},{"type":"Line","context":"comment","value":"// Works!","line":7,"start":155,"end":164},{"type":"T_IDENTIFIER","context":"normal","value":"acceptsOptionalString","line":8,"start":165,"end":186},{"type":"T_LPAREN","context":"normal","value":"(","line":8,"start":186,"end":187},{"type":"T_NULL","context":"normal","value":"null","line":8,"start":187,"end":191},{"type":"T_RPAREN","context":"normal","value":")","line":8,"start":191,"end":192},{"type":"T_SEMICOLON","context":"normal","value":";","line":8,"start":192,"end":193},{"type":"Line","context":"comment","value":"// Error!","line":8,"start":199,"end":208},{"type":"T_IDENTIFIER","context":"normal","value":"acceptsOptionalString","line":9,"start":209,"end":230},{"type":"T_LPAREN","context":"normal","value":"(","line":9,"start":230,"end":231},{"type":"T_RPAREN","context":"normal","value":")","line":9,"start":231,"end":232},{"type":"T_SEMICOLON","context":"normal","value":";","line":9,"start":232,"end":233},{"type":"Line","context":"comment","value":"// Works!","line":9,"start":243,"end":252}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot call `acceptsOptionalString` with `null` bound to `value` because null [1] is incompatible with string [2]. [incompatible-call]","context":"acceptsOptionalString(null); // Error!","source":"-","start":{"line":8,"column":23,"offset":187},"end":{"line":8,"column":26,"offset":191}}],"operation":null}]}
Symbols
Symbols are created with Symbol()
in JavaScript. Flow has basic support for symbols, using the symbol
type.
1
2
3
4
5
6
7
8
|
function acceptsSymbol(value: symbol) {
}
acceptsSymbol(Symbol());
acceptsSymbol(Symbol.isConcatSpreadable);
acceptsSymbol(false);
|
Cannot call `acceptsSymbol` with `false` bound to `value` because boolean [1] is incompatible with symbol [2]. [incompatible-call]
{"value":"// @flow\nfunction acceptsSymbol(value: symbol) {\n // ...\n}\n\nacceptsSymbol(Symbol()); // Works!\nacceptsSymbol(Symbol.isConcatSpreadable); // Works!\nacceptsSymbol(false); // Error!\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":"acceptsSymbol","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_SYMBOL_TYPE","context":"type","value":"symbol","line":2,"start":39,"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":"acceptsSymbol","line":6,"start":61,"end":74},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":74,"end":75},{"type":"T_IDENTIFIER","context":"normal","value":"Symbol","line":6,"start":75,"end":81},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":81,"end":82},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":82,"end":83},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":83,"end":84},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":84,"end":85},{"type":"Line","context":"comment","value":"// Works!","line":6,"start":86,"end":95},{"type":"T_IDENTIFIER","context":"normal","value":"acceptsSymbol","line":7,"start":96,"end":109},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":109,"end":110},{"type":"T_IDENTIFIER","context":"normal","value":"Symbol","line":7,"start":110,"end":116},{"type":"T_PERIOD","context":"normal","value":".","line":7,"start":116,"end":117},{"type":"T_IDENTIFIER","context":"normal","value":"isConcatSpreadable","line":7,"start":117,"end":135},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":135,"end":136},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":136,"end":137},{"type":"Line","context":"comment","value":"// Works!","line":7,"start":138,"end":147},{"type":"T_IDENTIFIER","context":"normal","value":"acceptsSymbol","line":8,"start":148,"end":161},{"type":"T_LPAREN","context":"normal","value":"(","line":8,"start":161,"end":162},{"type":"T_FALSE","context":"normal","value":"false","line":8,"start":162,"end":167},{"type":"T_RPAREN","context":"normal","value":")","line":8,"start":167,"end":168},{"type":"T_SEMICOLON","context":"normal","value":";","line":8,"start":168,"end":169},{"type":"Line","context":"comment","value":"// Error!","line":8,"start":170,"end":179}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot call `acceptsSymbol` with `false` bound to `value` because boolean [1] is incompatible with symbol [2]. [incompatible-call]","context":"acceptsSymbol(false); // Error!","source":"-","start":{"line":8,"column":15,"offset":162},"end":{"line":8,"column":19,"offset":167}}],"operation":null}]}
You can use typeof x === "symbol"
to refine to a symbol.
const x: symbol | number = Symbol();
if (typeof x === "symbol") {
const y: symbol = x;
} else {
const z: number = x;
}