Sometimes it is useful to create a type which is all of a set of other
types. For example, you might want to write a function which accepts an object
which is the combination of other object types. For this, Flow supports
intersection types.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
type A = { a: number };
type B = { b: boolean };
type C = { c: string };
function method(value: A & B & C) {
}
method({ a: 1 });
method({ a: 1, b: true });
method({ a: 1, b: true, c: 'three' });
|
Cannot call `method` with object literal bound to `value` because property `b` is missing in object literal [1] but exists in `B` [2]. [prop-missing]
Cannot call `method` with object literal bound to `value` because property `c` is missing in object literal [1] but exists in `C` [2]. [prop-missing]
Cannot call `method` with object literal bound to `value` because property `c` is missing in object literal [1] but exists in `C` [2]. [prop-missing]
{"value":"// @flow\ntype A = { a: number };\ntype B = { b: boolean };\ntype C = { c: string };\n\nfunction method(value: A & B & C) {\n // ...\n}\n\n// $ExpectError\nmethod({ a: 1 }); // Error!\n// $ExpectError\nmethod({ a: 1, b: true }); // Error!\nmethod({ a: 1, b: true, c: 'three' }); // Works!\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_TYPE","context":"normal","value":"type","line":2,"start":9,"end":13},{"type":"T_IDENTIFIER","context":"type","value":"A","line":2,"start":14,"end":15},{"type":"T_ASSIGN","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":"a","line":2,"start":20,"end":21},{"type":"T_COLON","context":"type","value":":","line":2,"start":21,"end":22},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":2,"start":23,"end":29},{"type":"T_RCURLY","context":"type","value":"}","line":2,"start":30,"end":31},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":31,"end":32},{"type":"T_TYPE","context":"normal","value":"type","line":3,"start":33,"end":37},{"type":"T_IDENTIFIER","context":"type","value":"B","line":3,"start":38,"end":39},{"type":"T_ASSIGN","context":"type","value":"=","line":3,"start":40,"end":41},{"type":"T_LCURLY","context":"type","value":"{","line":3,"start":42,"end":43},{"type":"T_IDENTIFIER","context":"normal","value":"b","line":3,"start":44,"end":45},{"type":"T_COLON","context":"type","value":":","line":3,"start":45,"end":46},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":3,"start":47,"end":54},{"type":"T_RCURLY","context":"type","value":"}","line":3,"start":55,"end":56},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":56,"end":57},{"type":"T_TYPE","context":"normal","value":"type","line":4,"start":58,"end":62},{"type":"T_IDENTIFIER","context":"type","value":"C","line":4,"start":63,"end":64},{"type":"T_ASSIGN","context":"type","value":"=","line":4,"start":65,"end":66},{"type":"T_LCURLY","context":"type","value":"{","line":4,"start":67,"end":68},{"type":"T_IDENTIFIER","context":"normal","value":"c","line":4,"start":69,"end":70},{"type":"T_COLON","context":"type","value":":","line":4,"start":70,"end":71},{"type":"T_STRING_TYPE","context":"type","value":"string","line":4,"start":72,"end":78},{"type":"T_RCURLY","context":"type","value":"}","line":4,"start":79,"end":80},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":80,"end":81},{"type":"T_FUNCTION","context":"normal","value":"function","line":6,"start":83,"end":91},{"type":"T_IDENTIFIER","context":"normal","value":"method","line":6,"start":92,"end":98},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":98,"end":99},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":6,"start":99,"end":104},{"type":"T_COLON","context":"type","value":":","line":6,"start":104,"end":105},{"type":"T_IDENTIFIER","context":"type","value":"A","line":6,"start":106,"end":107},{"type":"T_BIT_AND","context":"type","value":"&","line":6,"start":108,"end":109},{"type":"T_IDENTIFIER","context":"type","value":"B","line":6,"start":110,"end":111},{"type":"T_BIT_AND","context":"type","value":"&","line":6,"start":112,"end":113},{"type":"T_IDENTIFIER","context":"type","value":"C","line":6,"start":114,"end":115},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":115,"end":116},{"type":"T_LCURLY","context":"normal","value":"{","line":6,"start":117,"end":118},{"type":"Line","context":"comment","value":"// ...","line":7,"start":121,"end":127},{"type":"T_RCURLY","context":"normal","value":"}","line":8,"start":128,"end":129},{"type":"Line","context":"comment","value":"// $ExpectError","line":10,"start":131,"end":146},{"type":"T_IDENTIFIER","context":"normal","value":"method","line":11,"start":147,"end":153},{"type":"T_LPAREN","context":"normal","value":"(","line":11,"start":153,"end":154},{"type":"T_LCURLY","context":"normal","value":"{","line":11,"start":154,"end":155},{"type":"T_IDENTIFIER","context":"normal","value":"a","line":11,"start":156,"end":157},{"type":"T_COLON","context":"normal","value":":","line":11,"start":157,"end":158},{"type":"T_NUMBER","context":"normal","value":"1","line":11,"start":159,"end":160},{"type":"T_RCURLY","context":"normal","value":"}","line":11,"start":161,"end":162},{"type":"T_RPAREN","context":"normal","value":")","line":11,"start":162,"end":163},{"type":"T_SEMICOLON","context":"normal","value":";","line":11,"start":163,"end":164},{"type":"Line","context":"comment","value":"// Error!","line":11,"start":165,"end":174},{"type":"Line","context":"comment","value":"// $ExpectError","line":12,"start":175,"end":190},{"type":"T_IDENTIFIER","context":"normal","value":"method","line":13,"start":191,"end":197},{"type":"T_LPAREN","context":"normal","value":"(","line":13,"start":197,"end":198},{"type":"T_LCURLY","context":"normal","value":"{","line":13,"start":198,"end":199},{"type":"T_IDENTIFIER","context":"normal","value":"a","line":13,"start":200,"end":201},{"type":"T_COLON","context":"normal","value":":","line":13,"start":201,"end":202},{"type":"T_NUMBER","context":"normal","value":"1","line":13,"start":203,"end":204},{"type":"T_COMMA","context":"normal","value":",","line":13,"start":204,"end":205},{"type":"T_IDENTIFIER","context":"normal","value":"b","line":13,"start":206,"end":207},{"type":"T_COLON","context":"normal","value":":","line":13,"start":207,"end":208},{"type":"T_TRUE","context":"normal","value":"true","line":13,"start":209,"end":213},{"type":"T_RCURLY","context":"normal","value":"}","line":13,"start":214,"end":215},{"type":"T_RPAREN","context":"normal","value":")","line":13,"start":215,"end":216},{"type":"T_SEMICOLON","context":"normal","value":";","line":13,"start":216,"end":217},{"type":"Line","context":"comment","value":"// Error!","line":13,"start":218,"end":227},{"type":"T_IDENTIFIER","context":"normal","value":"method","line":14,"start":228,"end":234},{"type":"T_LPAREN","context":"normal","value":"(","line":14,"start":234,"end":235},{"type":"T_LCURLY","context":"normal","value":"{","line":14,"start":235,"end":236},{"type":"T_IDENTIFIER","context":"normal","value":"a","line":14,"start":237,"end":238},{"type":"T_COLON","context":"normal","value":":","line":14,"start":238,"end":239},{"type":"T_NUMBER","context":"normal","value":"1","line":14,"start":240,"end":241},{"type":"T_COMMA","context":"normal","value":",","line":14,"start":241,"end":242},{"type":"T_IDENTIFIER","context":"normal","value":"b","line":14,"start":243,"end":244},{"type":"T_COLON","context":"normal","value":":","line":14,"start":244,"end":245},{"type":"T_TRUE","context":"normal","value":"true","line":14,"start":246,"end":250},{"type":"T_COMMA","context":"normal","value":",","line":14,"start":250,"end":251},{"type":"T_IDENTIFIER","context":"normal","value":"c","line":14,"start":252,"end":253},{"type":"T_COLON","context":"normal","value":":","line":14,"start":253,"end":254},{"type":"T_STRING","context":"normal","value":"'three'","line":14,"start":255,"end":262},{"type":"T_RCURLY","context":"normal","value":"}","line":14,"start":263,"end":264},{"type":"T_RPAREN","context":"normal","value":")","line":14,"start":264,"end":265},{"type":"T_SEMICOLON","context":"normal","value":";","line":14,"start":265,"end":266},{"type":"Line","context":"comment","value":"// Works!","line":14,"start":267,"end":276}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot call `method` with object literal bound to `value` because property `b` is missing in object literal [1] but exists in `B` [2]. [prop-missing]","context":"method({ a: 1 }); // Error!","source":"-","start":{"line":11,"column":8,"offset":154},"end":{"line":11,"column":15,"offset":162}}],"operation":null},{"id":"E2","messages":[{"id":"E2M1","description":"Cannot call `method` with object literal bound to `value` because property `c` is missing in object literal [1] but exists in `C` [2]. [prop-missing]","context":"method({ a: 1 }); // Error!","source":"-","start":{"line":11,"column":8,"offset":154},"end":{"line":11,"column":15,"offset":162}}],"operation":null},{"id":"E3","messages":[{"id":"E3M1","description":"Cannot call `method` with object literal bound to `value` because property `c` is missing in object literal [1] but exists in `C` [2]. [prop-missing]","context":"method({ a: 1, b: true }); // Error!","source":"-","start":{"line":13,"column":8,"offset":198},"end":{"line":13,"column":24,"offset":215}}],"operation":null}]}
Intersection type syntax
Intersection types are any number of types which are joined by an ampersand &
.
1
|
Type1 & Type2 & ... & TypeN
|
{"value":"Type1 & Type2 & ... & TypeN\n","tokens":[{"type":"T_IDENTIFIER","context":"normal","value":"Type1","line":1,"start":0,"end":5},{"type":"T_BIT_AND","context":"normal","value":"&","line":1,"start":6,"end":7},{"type":"T_IDENTIFIER","context":"normal","value":"Type2","line":1,"start":8,"end":13},{"type":"T_BIT_AND","context":"normal","value":"&","line":1,"start":14,"end":15},{"type":"T_ELLIPSIS","context":"normal","value":"...","line":1,"start":16,"end":19},{"type":"T_BIT_AND","context":"normal","value":"&","line":1,"start":20,"end":21},{"type":"T_IDENTIFIER","context":"normal","value":"TypeN","line":1,"start":22,"end":27}],"errors":[]}
You may also add a leading ampersand which is useful when breaking intersection
types onto multiple lines.
1
2
3
4
5
|
type Foo =
& Type1
& Type2
& ...
& TypeN
|
{"value":"type Foo =\n & Type1\n & Type2\n & ...\n & TypeN\n","tokens":[{"type":"T_TYPE","context":"normal","value":"type","line":1,"start":0,"end":4},{"type":"T_IDENTIFIER","context":"type","value":"Foo","line":1,"start":5,"end":8},{"type":"T_ASSIGN","context":"type","value":"=","line":1,"start":9,"end":10},{"type":"T_BIT_AND","context":"type","value":"&","line":2,"start":13,"end":14},{"type":"T_IDENTIFIER","context":"type","value":"Type1","line":2,"start":15,"end":20},{"type":"T_BIT_AND","context":"type","value":"&","line":3,"start":23,"end":24},{"type":"T_IDENTIFIER","context":"type","value":"Type2","line":3,"start":25,"end":30},{"type":"T_BIT_AND","context":"type","value":"&","line":4,"start":33,"end":34},{"type":"T_ELLIPSIS","context":"normal","value":"...","line":4,"start":35,"end":38},{"type":"T_BIT_AND","context":"normal","value":"&","line":5,"start":41,"end":42},{"type":"T_IDENTIFIER","context":"normal","value":"TypeN","line":5,"start":43,"end":48}],"errors":[]}
Each of the members of a intersection type can be any type, even another
intersection type.
1
2
3
4
|
type Foo = Type1 & Type2;
type Bar = Type3 & Type4;
type Baz = Foo & Bar;
|
{"value":"type Foo = Type1 & Type2;\ntype Bar = Type3 & Type4;\n\ntype Baz = Foo & Bar;\n","tokens":[{"type":"T_TYPE","context":"normal","value":"type","line":1,"start":0,"end":4},{"type":"T_IDENTIFIER","context":"type","value":"Foo","line":1,"start":5,"end":8},{"type":"T_ASSIGN","context":"type","value":"=","line":1,"start":9,"end":10},{"type":"T_IDENTIFIER","context":"type","value":"Type1","line":1,"start":11,"end":16},{"type":"T_BIT_AND","context":"type","value":"&","line":1,"start":17,"end":18},{"type":"T_IDENTIFIER","context":"type","value":"Type2","line":1,"start":19,"end":24},{"type":"T_SEMICOLON","context":"normal","value":";","line":1,"start":24,"end":25},{"type":"T_TYPE","context":"normal","value":"type","line":2,"start":26,"end":30},{"type":"T_IDENTIFIER","context":"type","value":"Bar","line":2,"start":31,"end":34},{"type":"T_ASSIGN","context":"type","value":"=","line":2,"start":35,"end":36},{"type":"T_IDENTIFIER","context":"type","value":"Type3","line":2,"start":37,"end":42},{"type":"T_BIT_AND","context":"type","value":"&","line":2,"start":43,"end":44},{"type":"T_IDENTIFIER","context":"type","value":"Type4","line":2,"start":45,"end":50},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":50,"end":51},{"type":"T_TYPE","context":"normal","value":"type","line":4,"start":53,"end":57},{"type":"T_IDENTIFIER","context":"type","value":"Baz","line":4,"start":58,"end":61},{"type":"T_ASSIGN","context":"type","value":"=","line":4,"start":62,"end":63},{"type":"T_IDENTIFIER","context":"type","value":"Foo","line":4,"start":64,"end":67},{"type":"T_BIT_AND","context":"type","value":"&","line":4,"start":68,"end":69},{"type":"T_IDENTIFIER","context":"type","value":"Bar","line":4,"start":70,"end":73},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":73,"end":74}],"errors":[]}
Intersection types require all in, but one out
Intersection types are the opposite of union types. When calling a function
that accepts an intersection type, we must pass in all of those types. But
inside of our function we only have to treat it as any one of those
types.
1
2
3
4
5
6
7
8
9
10
|
type A = { a: number };
type B = { b: boolean };
type C = { c: string };
function method(value: A & B & C) {
var a: A = value;
var b: B = value;
var c: C = value;
}
|
{"value":"// @flow\ntype A = { a: number };\ntype B = { b: boolean };\ntype C = { c: string };\n\nfunction method(value: A & B & C) {\n var a: A = value;\n var b: B = value;\n var c: C = value;\n}\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_TYPE","context":"normal","value":"type","line":2,"start":9,"end":13},{"type":"T_IDENTIFIER","context":"type","value":"A","line":2,"start":14,"end":15},{"type":"T_ASSIGN","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":"a","line":2,"start":20,"end":21},{"type":"T_COLON","context":"type","value":":","line":2,"start":21,"end":22},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":2,"start":23,"end":29},{"type":"T_RCURLY","context":"type","value":"}","line":2,"start":30,"end":31},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":31,"end":32},{"type":"T_TYPE","context":"normal","value":"type","line":3,"start":33,"end":37},{"type":"T_IDENTIFIER","context":"type","value":"B","line":3,"start":38,"end":39},{"type":"T_ASSIGN","context":"type","value":"=","line":3,"start":40,"end":41},{"type":"T_LCURLY","context":"type","value":"{","line":3,"start":42,"end":43},{"type":"T_IDENTIFIER","context":"normal","value":"b","line":3,"start":44,"end":45},{"type":"T_COLON","context":"type","value":":","line":3,"start":45,"end":46},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":3,"start":47,"end":54},{"type":"T_RCURLY","context":"type","value":"}","line":3,"start":55,"end":56},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":56,"end":57},{"type":"T_TYPE","context":"normal","value":"type","line":4,"start":58,"end":62},{"type":"T_IDENTIFIER","context":"type","value":"C","line":4,"start":63,"end":64},{"type":"T_ASSIGN","context":"type","value":"=","line":4,"start":65,"end":66},{"type":"T_LCURLY","context":"type","value":"{","line":4,"start":67,"end":68},{"type":"T_IDENTIFIER","context":"normal","value":"c","line":4,"start":69,"end":70},{"type":"T_COLON","context":"type","value":":","line":4,"start":70,"end":71},{"type":"T_STRING_TYPE","context":"type","value":"string","line":4,"start":72,"end":78},{"type":"T_RCURLY","context":"type","value":"}","line":4,"start":79,"end":80},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":80,"end":81},{"type":"T_FUNCTION","context":"normal","value":"function","line":6,"start":83,"end":91},{"type":"T_IDENTIFIER","context":"normal","value":"method","line":6,"start":92,"end":98},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":98,"end":99},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":6,"start":99,"end":104},{"type":"T_COLON","context":"type","value":":","line":6,"start":104,"end":105},{"type":"T_IDENTIFIER","context":"type","value":"A","line":6,"start":106,"end":107},{"type":"T_BIT_AND","context":"type","value":"&","line":6,"start":108,"end":109},{"type":"T_IDENTIFIER","context":"type","value":"B","line":6,"start":110,"end":111},{"type":"T_BIT_AND","context":"type","value":"&","line":6,"start":112,"end":113},{"type":"T_IDENTIFIER","context":"type","value":"C","line":6,"start":114,"end":115},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":115,"end":116},{"type":"T_LCURLY","context":"normal","value":"{","line":6,"start":117,"end":118},{"type":"T_VAR","context":"normal","value":"var","line":7,"start":121,"end":124},{"type":"T_IDENTIFIER","context":"normal","value":"a","line":7,"start":125,"end":126},{"type":"T_COLON","context":"type","value":":","line":7,"start":126,"end":127},{"type":"T_IDENTIFIER","context":"type","value":"A","line":7,"start":128,"end":129},{"type":"T_ASSIGN","context":"normal","value":"=","line":7,"start":130,"end":131},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":7,"start":132,"end":137},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":137,"end":138},{"type":"T_VAR","context":"normal","value":"var","line":8,"start":141,"end":144},{"type":"T_IDENTIFIER","context":"normal","value":"b","line":8,"start":145,"end":146},{"type":"T_COLON","context":"type","value":":","line":8,"start":146,"end":147},{"type":"T_IDENTIFIER","context":"type","value":"B","line":8,"start":148,"end":149},{"type":"T_ASSIGN","context":"normal","value":"=","line":8,"start":150,"end":151},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":8,"start":152,"end":157},{"type":"T_SEMICOLON","context":"normal","value":";","line":8,"start":157,"end":158},{"type":"T_VAR","context":"normal","value":"var","line":9,"start":161,"end":164},{"type":"T_IDENTIFIER","context":"normal","value":"c","line":9,"start":165,"end":166},{"type":"T_COLON","context":"type","value":":","line":9,"start":166,"end":167},{"type":"T_IDENTIFIER","context":"type","value":"C","line":9,"start":168,"end":169},{"type":"T_ASSIGN","context":"normal","value":"=","line":9,"start":170,"end":171},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":9,"start":172,"end":177},{"type":"T_SEMICOLON","context":"normal","value":";","line":9,"start":177,"end":178},{"type":"T_RCURLY","context":"normal","value":"}","line":10,"start":179,"end":180}],"errors":[]}
Even as we treat our value as just one of the types, we do not get an error
because it satisfies all of them.
Intersection of function types
A common use of intersection types is to express functions that return
different results based on the input we pass in. Suppose for example
that we want to write the type of a function that
- returns a string, when we pass in the value
"string"
,
- returns a number, when we pass in the value
"number"
, and
- returns any possible type (
mixed
), when we pass in any other string.
The type of this function will be
1
2
3
4
|
type Fn =
& ((x: "string") => string)
& ((x: "number") => number)
& ((x: string) => null);
|
{"value":"type Fn =\n & ((x: \"string\") => string)\n & ((x: \"number\") => number)\n & ((x: string) => null);\n","tokens":[{"type":"T_TYPE","context":"normal","value":"type","line":1,"start":0,"end":4},{"type":"T_IDENTIFIER","context":"type","value":"Fn","line":1,"start":5,"end":7},{"type":"T_ASSIGN","context":"type","value":"=","line":1,"start":8,"end":9},{"type":"T_BIT_AND","context":"type","value":"&","line":2,"start":12,"end":13},{"type":"T_LPAREN","context":"type","value":"(","line":2,"start":14,"end":15},{"type":"T_LPAREN","context":"type","value":"(","line":2,"start":15,"end":16},{"type":"T_IDENTIFIER","context":"normal","value":"x","line":2,"start":16,"end":17},{"type":"T_COLON","context":"type","value":":","line":2,"start":17,"end":18},{"type":"T_STRING","context":"type","value":"\"string\"","line":2,"start":19,"end":27},{"type":"T_RPAREN","context":"type","value":")","line":2,"start":27,"end":28},{"type":"T_ARROW","context":"type","value":"=>","line":2,"start":29,"end":31},{"type":"T_STRING_TYPE","context":"type","value":"string","line":2,"start":32,"end":38},{"type":"T_RPAREN","context":"type","value":")","line":2,"start":38,"end":39},{"type":"T_BIT_AND","context":"type","value":"&","line":3,"start":42,"end":43},{"type":"T_LPAREN","context":"type","value":"(","line":3,"start":44,"end":45},{"type":"T_LPAREN","context":"type","value":"(","line":3,"start":45,"end":46},{"type":"T_IDENTIFIER","context":"normal","value":"x","line":3,"start":46,"end":47},{"type":"T_COLON","context":"type","value":":","line":3,"start":47,"end":48},{"type":"T_STRING","context":"type","value":"\"number\"","line":3,"start":49,"end":57},{"type":"T_RPAREN","context":"type","value":")","line":3,"start":57,"end":58},{"type":"T_ARROW","context":"type","value":"=>","line":3,"start":59,"end":61},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":3,"start":62,"end":68},{"type":"T_RPAREN","context":"type","value":")","line":3,"start":68,"end":69},{"type":"T_BIT_AND","context":"type","value":"&","line":4,"start":72,"end":73},{"type":"T_LPAREN","context":"type","value":"(","line":4,"start":74,"end":75},{"type":"T_LPAREN","context":"type","value":"(","line":4,"start":75,"end":76},{"type":"T_IDENTIFIER","context":"normal","value":"x","line":4,"start":76,"end":77},{"type":"T_COLON","context":"type","value":":","line":4,"start":77,"end":78},{"type":"T_STRING_TYPE","context":"type","value":"string","line":4,"start":79,"end":85},{"type":"T_RPAREN","context":"type","value":")","line":4,"start":85,"end":86},{"type":"T_ARROW","context":"type","value":"=>","line":4,"start":87,"end":89},{"type":"T_NULL","context":"type","value":"null","line":4,"start":90,"end":94},{"type":"T_RPAREN","context":"type","value":")","line":4,"start":94,"end":95},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":95,"end":96}],"errors":[]}
Each line in the above definition is called an overload, and we say that functions
of type Fn
are overloaded.
Note the use of parentheses around the arrow types. These are necessary to override
the precedence of the “arrow” constructor over the intersection.
Calling an overloaded function
Using the above definition we can declare a function fn
that has the following behavior:
1
2
3
4
|
declare var fn: Fn;
var n: string = fn("string");
var n: number = fn("number");
var n: boolean = fn("boolean");
|
{"value":"declare var fn: Fn;\nvar n: string = fn(\"string\"); // okay\nvar n: number = fn(\"number\"); // okay\nvar n: boolean = fn(\"boolean\"); // error: null is incompatible with number\n","tokens":[{"type":"T_DECLARE","context":"normal","value":"declare","line":1,"start":0,"end":7},{"type":"T_VAR","context":"normal","value":"var","line":1,"start":8,"end":11},{"type":"T_IDENTIFIER","context":"normal","value":"fn","line":1,"start":12,"end":14},{"type":"T_COLON","context":"type","value":":","line":1,"start":14,"end":15},{"type":"T_IDENTIFIER","context":"type","value":"Fn","line":1,"start":16,"end":18},{"type":"T_SEMICOLON","context":"normal","value":";","line":1,"start":18,"end":19},{"type":"T_VAR","context":"normal","value":"var","line":2,"start":20,"end":23},{"type":"T_IDENTIFIER","context":"normal","value":"n","line":2,"start":24,"end":25},{"type":"T_COLON","context":"type","value":":","line":2,"start":25,"end":26},{"type":"T_STRING_TYPE","context":"type","value":"string","line":2,"start":27,"end":33},{"type":"T_ASSIGN","context":"normal","value":"=","line":2,"start":34,"end":35},{"type":"T_IDENTIFIER","context":"normal","value":"fn","line":2,"start":36,"end":38},{"type":"T_LPAREN","context":"normal","value":"(","line":2,"start":38,"end":39},{"type":"T_STRING","context":"normal","value":"\"string\"","line":2,"start":39,"end":47},{"type":"T_RPAREN","context":"normal","value":")","line":2,"start":47,"end":48},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":48,"end":49},{"type":"Line","context":"comment","value":"// okay","line":2,"start":50,"end":57},{"type":"T_VAR","context":"normal","value":"var","line":3,"start":58,"end":61},{"type":"T_IDENTIFIER","context":"normal","value":"n","line":3,"start":62,"end":63},{"type":"T_COLON","context":"type","value":":","line":3,"start":63,"end":64},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":3,"start":65,"end":71},{"type":"T_ASSIGN","context":"normal","value":"=","line":3,"start":72,"end":73},{"type":"T_IDENTIFIER","context":"normal","value":"fn","line":3,"start":74,"end":76},{"type":"T_LPAREN","context":"normal","value":"(","line":3,"start":76,"end":77},{"type":"T_STRING","context":"normal","value":"\"number\"","line":3,"start":77,"end":85},{"type":"T_RPAREN","context":"normal","value":")","line":3,"start":85,"end":86},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":86,"end":87},{"type":"Line","context":"comment","value":"// okay","line":3,"start":88,"end":95},{"type":"T_VAR","context":"normal","value":"var","line":4,"start":96,"end":99},{"type":"T_IDENTIFIER","context":"normal","value":"n","line":4,"start":100,"end":101},{"type":"T_COLON","context":"type","value":":","line":4,"start":101,"end":102},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":4,"start":103,"end":110},{"type":"T_ASSIGN","context":"normal","value":"=","line":4,"start":111,"end":112},{"type":"T_IDENTIFIER","context":"normal","value":"fn","line":4,"start":113,"end":115},{"type":"T_LPAREN","context":"normal","value":"(","line":4,"start":115,"end":116},{"type":"T_STRING","context":"normal","value":"\"boolean\"","line":4,"start":116,"end":125},{"type":"T_RPAREN","context":"normal","value":")","line":4,"start":125,"end":126},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":126,"end":127},{"type":"Line","context":"comment","value":"// error: null is incompatible with number","line":4,"start":128,"end":170}],"errors":[]}
Flow achieves this behavior by matching the type of the argument to the first
overload with a compatible parameter type. Notice for example that the argument
"string"
matches both the first and the last overload. Flow will
just pick the first one. If no overload matches, Flow will raise an error at the
call site.
Declaring overloaded functions
An equivalent way to declare the same function fn
would be by using consecutive
“declare function” statements
1
2
3
|
declare function fn(x: "string"): string;
declare function fn(x: "number"): number;
declare function fn(x: string): null;
|
{"value":"declare function fn(x: \"string\"): string;\ndeclare function fn(x: \"number\"): number;\ndeclare function fn(x: string): null;\n","tokens":[{"type":"T_DECLARE","context":"normal","value":"declare","line":1,"start":0,"end":7},{"type":"T_FUNCTION","context":"normal","value":"function","line":1,"start":8,"end":16},{"type":"T_IDENTIFIER","context":"normal","value":"fn","line":1,"start":17,"end":19},{"type":"T_LPAREN","context":"type","value":"(","line":1,"start":19,"end":20},{"type":"T_IDENTIFIER","context":"normal","value":"x","line":1,"start":20,"end":21},{"type":"T_COLON","context":"type","value":":","line":1,"start":21,"end":22},{"type":"T_STRING","context":"type","value":"\"string\"","line":1,"start":23,"end":31},{"type":"T_RPAREN","context":"type","value":")","line":1,"start":31,"end":32},{"type":"T_COLON","context":"normal","value":":","line":1,"start":32,"end":33},{"type":"T_STRING_TYPE","context":"type","value":"string","line":1,"start":34,"end":40},{"type":"T_SEMICOLON","context":"normal","value":";","line":1,"start":40,"end":41},{"type":"T_DECLARE","context":"normal","value":"declare","line":2,"start":42,"end":49},{"type":"T_FUNCTION","context":"normal","value":"function","line":2,"start":50,"end":58},{"type":"T_IDENTIFIER","context":"normal","value":"fn","line":2,"start":59,"end":61},{"type":"T_LPAREN","context":"type","value":"(","line":2,"start":61,"end":62},{"type":"T_IDENTIFIER","context":"normal","value":"x","line":2,"start":62,"end":63},{"type":"T_COLON","context":"type","value":":","line":2,"start":63,"end":64},{"type":"T_STRING","context":"type","value":"\"number\"","line":2,"start":65,"end":73},{"type":"T_RPAREN","context":"type","value":")","line":2,"start":73,"end":74},{"type":"T_COLON","context":"normal","value":":","line":2,"start":74,"end":75},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":2,"start":76,"end":82},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":82,"end":83},{"type":"T_DECLARE","context":"normal","value":"declare","line":3,"start":84,"end":91},{"type":"T_FUNCTION","context":"normal","value":"function","line":3,"start":92,"end":100},{"type":"T_IDENTIFIER","context":"normal","value":"fn","line":3,"start":101,"end":103},{"type":"T_LPAREN","context":"type","value":"(","line":3,"start":103,"end":104},{"type":"T_IDENTIFIER","context":"normal","value":"x","line":3,"start":104,"end":105},{"type":"T_COLON","context":"type","value":":","line":3,"start":105,"end":106},{"type":"T_STRING_TYPE","context":"type","value":"string","line":3,"start":107,"end":113},{"type":"T_RPAREN","context":"type","value":")","line":3,"start":113,"end":114},{"type":"T_COLON","context":"normal","value":":","line":3,"start":114,"end":115},{"type":"T_NULL","context":"type","value":"null","line":3,"start":116,"end":120},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":120,"end":121}],"errors":[]}
A limitation in Flow is that it can’t check the body of a function against
an intersection type. In other words, if we provided the following implementation
for fn
right after the above declarations
1
2
3
4
5
|
function fn(x) {
if (x === "string") { return ""; }
else if (x === "number") { return 0; }
else { return null; }
}
|
{"value":"function fn(x) {\n if (x === \"string\") { return \"\"; }\n else if (x === \"number\") { return 0; }\n else { return null; }\n}\n","tokens":[{"type":"T_FUNCTION","context":"normal","value":"function","line":1,"start":0,"end":8},{"type":"T_IDENTIFIER","context":"normal","value":"fn","line":1,"start":9,"end":11},{"type":"T_LPAREN","context":"normal","value":"(","line":1,"start":11,"end":12},{"type":"T_IDENTIFIER","context":"normal","value":"x","line":1,"start":12,"end":13},{"type":"T_RPAREN","context":"normal","value":")","line":1,"start":13,"end":14},{"type":"T_LCURLY","context":"normal","value":"{","line":1,"start":15,"end":16},{"type":"T_IF","context":"normal","value":"if","line":2,"start":19,"end":21},{"type":"T_LPAREN","context":"normal","value":"(","line":2,"start":22,"end":23},{"type":"T_IDENTIFIER","context":"normal","value":"x","line":2,"start":23,"end":24},{"type":"T_STRICT_EQUAL","context":"normal","value":"===","line":2,"start":25,"end":28},{"type":"T_STRING","context":"normal","value":"\"string\"","line":2,"start":29,"end":37},{"type":"T_RPAREN","context":"normal","value":")","line":2,"start":37,"end":38},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":39,"end":40},{"type":"T_RETURN","context":"normal","value":"return","line":2,"start":41,"end":47},{"type":"T_STRING","context":"normal","value":"\"\"","line":2,"start":48,"end":50},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":50,"end":51},{"type":"T_RCURLY","context":"normal","value":"}","line":2,"start":52,"end":53},{"type":"T_ELSE","context":"normal","value":"else","line":3,"start":56,"end":60},{"type":"T_IF","context":"normal","value":"if","line":3,"start":61,"end":63},{"type":"T_LPAREN","context":"normal","value":"(","line":3,"start":64,"end":65},{"type":"T_IDENTIFIER","context":"normal","value":"x","line":3,"start":65,"end":66},{"type":"T_STRICT_EQUAL","context":"normal","value":"===","line":3,"start":67,"end":70},{"type":"T_STRING","context":"normal","value":"\"number\"","line":3,"start":71,"end":79},{"type":"T_RPAREN","context":"normal","value":")","line":3,"start":79,"end":80},{"type":"T_LCURLY","context":"normal","value":"{","line":3,"start":81,"end":82},{"type":"T_RETURN","context":"normal","value":"return","line":3,"start":83,"end":89},{"type":"T_NUMBER","context":"normal","value":"0","line":3,"start":90,"end":91},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":91,"end":92},{"type":"T_RCURLY","context":"normal","value":"}","line":3,"start":93,"end":94},{"type":"T_ELSE","context":"normal","value":"else","line":4,"start":97,"end":101},{"type":"T_LCURLY","context":"normal","value":"{","line":4,"start":102,"end":103},{"type":"T_RETURN","context":"normal","value":"return","line":4,"start":104,"end":110},{"type":"T_NULL","context":"normal","value":"null","line":4,"start":111,"end":115},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":115,"end":116},{"type":"T_RCURLY","context":"normal","value":"}","line":4,"start":117,"end":118},{"type":"T_RCURLY","context":"normal","value":"}","line":5,"start":119,"end":120}],"errors":[]}
Flow silently accepts it (and uses Fn
as the inferred type), but does not check
the implementation against this signature. This makes this kind of declaration
a better suited candidate for library definitions, where implementations are omitted.
Intersections of object types
When you create an intersection of object types, you merge all of their
properties together.
For example, when you create an intersection of two objects with different sets
of properties, it will result in an object with all of the properties.
1
2
3
4
5
6
7
8
9
10
|
type One = { foo: number };
type Two = { bar: boolean };
type Both = One & Two;
var value: Both = {
foo: 1,
bar: true
};
|
{"value":"// @flow\ntype One = { foo: number };\ntype Two = { bar: boolean };\n\ntype Both = One & Two;\n\nvar value: Both = {\n foo: 1,\n bar: true\n};\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_TYPE","context":"normal","value":"type","line":2,"start":9,"end":13},{"type":"T_IDENTIFIER","context":"type","value":"One","line":2,"start":14,"end":17},{"type":"T_ASSIGN","context":"type","value":"=","line":2,"start":18,"end":19},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":20,"end":21},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":2,"start":22,"end":25},{"type":"T_COLON","context":"type","value":":","line":2,"start":25,"end":26},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":2,"start":27,"end":33},{"type":"T_RCURLY","context":"type","value":"}","line":2,"start":34,"end":35},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":35,"end":36},{"type":"T_TYPE","context":"normal","value":"type","line":3,"start":37,"end":41},{"type":"T_IDENTIFIER","context":"type","value":"Two","line":3,"start":42,"end":45},{"type":"T_ASSIGN","context":"type","value":"=","line":3,"start":46,"end":47},{"type":"T_LCURLY","context":"type","value":"{","line":3,"start":48,"end":49},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":3,"start":50,"end":53},{"type":"T_COLON","context":"type","value":":","line":3,"start":53,"end":54},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":3,"start":55,"end":62},{"type":"T_RCURLY","context":"type","value":"}","line":3,"start":63,"end":64},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":64,"end":65},{"type":"T_TYPE","context":"normal","value":"type","line":5,"start":67,"end":71},{"type":"T_IDENTIFIER","context":"type","value":"Both","line":5,"start":72,"end":76},{"type":"T_ASSIGN","context":"type","value":"=","line":5,"start":77,"end":78},{"type":"T_IDENTIFIER","context":"type","value":"One","line":5,"start":79,"end":82},{"type":"T_BIT_AND","context":"type","value":"&","line":5,"start":83,"end":84},{"type":"T_IDENTIFIER","context":"type","value":"Two","line":5,"start":85,"end":88},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":88,"end":89},{"type":"T_VAR","context":"normal","value":"var","line":7,"start":91,"end":94},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":7,"start":95,"end":100},{"type":"T_COLON","context":"type","value":":","line":7,"start":100,"end":101},{"type":"T_IDENTIFIER","context":"type","value":"Both","line":7,"start":102,"end":106},{"type":"T_ASSIGN","context":"normal","value":"=","line":7,"start":107,"end":108},{"type":"T_LCURLY","context":"normal","value":"{","line":7,"start":109,"end":110},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":8,"start":113,"end":116},{"type":"T_COLON","context":"normal","value":":","line":8,"start":116,"end":117},{"type":"T_NUMBER","context":"normal","value":"1","line":8,"start":118,"end":119},{"type":"T_COMMA","context":"normal","value":",","line":8,"start":119,"end":120},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":9,"start":123,"end":126},{"type":"T_COLON","context":"normal","value":":","line":9,"start":126,"end":127},{"type":"T_TRUE","context":"normal","value":"true","line":9,"start":128,"end":132},{"type":"T_RCURLY","context":"normal","value":"}","line":10,"start":133,"end":134},{"type":"T_SEMICOLON","context":"normal","value":";","line":10,"start":134,"end":135}],"errors":[]}
When you have properties that overlap by having the same name, Flow follows the same
strategy as with overloaded functions: it will return the type of the first property
that matches this name.
For example, if you merge two objects with a property named prop
, first with a
type of number and second with a type of boolean, accessing prop
will return
number
.
1
2
3
4
5
6
7
|
type One = { prop: number };
type Two = { prop: boolean };
declare var both: One & Two;
var prop1: number = both.prop;
var prop2: boolean = both.prop;
|
{"value":"type One = { prop: number };\ntype Two = { prop: boolean };\n\ndeclare var both: One & Two;\n\nvar prop1: number = both.prop; // okay\nvar prop2: boolean = both.prop; // Error: number is incompatible with boolean\n","tokens":[{"type":"T_TYPE","context":"normal","value":"type","line":1,"start":0,"end":4},{"type":"T_IDENTIFIER","context":"type","value":"One","line":1,"start":5,"end":8},{"type":"T_ASSIGN","context":"type","value":"=","line":1,"start":9,"end":10},{"type":"T_LCURLY","context":"type","value":"{","line":1,"start":11,"end":12},{"type":"T_IDENTIFIER","context":"normal","value":"prop","line":1,"start":13,"end":17},{"type":"T_COLON","context":"type","value":":","line":1,"start":17,"end":18},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":1,"start":19,"end":25},{"type":"T_RCURLY","context":"type","value":"}","line":1,"start":26,"end":27},{"type":"T_SEMICOLON","context":"normal","value":";","line":1,"start":27,"end":28},{"type":"T_TYPE","context":"normal","value":"type","line":2,"start":29,"end":33},{"type":"T_IDENTIFIER","context":"type","value":"Two","line":2,"start":34,"end":37},{"type":"T_ASSIGN","context":"type","value":"=","line":2,"start":38,"end":39},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":40,"end":41},{"type":"T_IDENTIFIER","context":"normal","value":"prop","line":2,"start":42,"end":46},{"type":"T_COLON","context":"type","value":":","line":2,"start":46,"end":47},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":2,"start":48,"end":55},{"type":"T_RCURLY","context":"type","value":"}","line":2,"start":56,"end":57},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":57,"end":58},{"type":"T_DECLARE","context":"normal","value":"declare","line":4,"start":60,"end":67},{"type":"T_VAR","context":"normal","value":"var","line":4,"start":68,"end":71},{"type":"T_IDENTIFIER","context":"normal","value":"both","line":4,"start":72,"end":76},{"type":"T_COLON","context":"type","value":":","line":4,"start":76,"end":77},{"type":"T_IDENTIFIER","context":"type","value":"One","line":4,"start":78,"end":81},{"type":"T_BIT_AND","context":"type","value":"&","line":4,"start":82,"end":83},{"type":"T_IDENTIFIER","context":"type","value":"Two","line":4,"start":84,"end":87},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":87,"end":88},{"type":"T_VAR","context":"normal","value":"var","line":6,"start":90,"end":93},{"type":"T_IDENTIFIER","context":"normal","value":"prop1","line":6,"start":94,"end":99},{"type":"T_COLON","context":"type","value":":","line":6,"start":99,"end":100},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":6,"start":101,"end":107},{"type":"T_ASSIGN","context":"normal","value":"=","line":6,"start":108,"end":109},{"type":"T_IDENTIFIER","context":"normal","value":"both","line":6,"start":110,"end":114},{"type":"T_PERIOD","context":"normal","value":".","line":6,"start":114,"end":115},{"type":"T_IDENTIFIER","context":"normal","value":"prop","line":6,"start":115,"end":119},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":119,"end":120},{"type":"Line","context":"comment","value":"// okay","line":6,"start":121,"end":128},{"type":"T_VAR","context":"normal","value":"var","line":7,"start":129,"end":132},{"type":"T_IDENTIFIER","context":"normal","value":"prop2","line":7,"start":133,"end":138},{"type":"T_COLON","context":"type","value":":","line":7,"start":138,"end":139},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":7,"start":140,"end":147},{"type":"T_ASSIGN","context":"normal","value":"=","line":7,"start":148,"end":149},{"type":"T_IDENTIFIER","context":"normal","value":"both","line":7,"start":150,"end":154},{"type":"T_PERIOD","context":"normal","value":".","line":7,"start":154,"end":155},{"type":"T_IDENTIFIER","context":"normal","value":"prop","line":7,"start":155,"end":159},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":159,"end":160},{"type":"Line","context":"comment","value":"// Error: number is incompatible with boolean","line":7,"start":161,"end":206}],"errors":[]}
Note: When it comes to objects, the order-specific way in which intersection
types are implemented in Flow, may often seem counterintuitive from a set theoretic
point of view. In sets, the operands of intersection can change order arbitrarily
(commutative property). For this reason, it is a better practice to define this
kind of operation over object types using the spread operator, e.g. { ...One, ...Two }
,
where the ordering semantics are better specified.
Impossible intersection types
Using intersection types, it is possible to create types which are impossible
to create at runtime. Intersection types will allow you to combine any set of
types, even ones that conflict with one another.
For example, you can create an intersection of a number and a string.
1
2
3
4
5
6
7
8
9
10
11
|
type NumberAndString = number & string;
function method(value: NumberAndString) {
}
method(3.14);
method('hi');
|
Cannot call `method` with `3.14` bound to `value` because number [1] is incompatible with string [2]. [incompatible-call]
Cannot call `method` with `'hi'` bound to `value` because string [1] is incompatible with number [2]. [incompatible-call]
{"value":"// @flow\ntype NumberAndString = number & string;\n\nfunction method(value: NumberAndString) {\n // ...\n}\n\n// $ExpectError\nmethod(3.14); // Error!\n// $ExpectError\nmethod('hi'); // Error!\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_TYPE","context":"normal","value":"type","line":2,"start":9,"end":13},{"type":"T_IDENTIFIER","context":"type","value":"NumberAndString","line":2,"start":14,"end":29},{"type":"T_ASSIGN","context":"type","value":"=","line":2,"start":30,"end":31},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":2,"start":32,"end":38},{"type":"T_BIT_AND","context":"type","value":"&","line":2,"start":39,"end":40},{"type":"T_STRING_TYPE","context":"type","value":"string","line":2,"start":41,"end":47},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":47,"end":48},{"type":"T_FUNCTION","context":"normal","value":"function","line":4,"start":50,"end":58},{"type":"T_IDENTIFIER","context":"normal","value":"method","line":4,"start":59,"end":65},{"type":"T_LPAREN","context":"normal","value":"(","line":4,"start":65,"end":66},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":4,"start":66,"end":71},{"type":"T_COLON","context":"type","value":":","line":4,"start":71,"end":72},{"type":"T_IDENTIFIER","context":"type","value":"NumberAndString","line":4,"start":73,"end":88},{"type":"T_RPAREN","context":"normal","value":")","line":4,"start":88,"end":89},{"type":"T_LCURLY","context":"normal","value":"{","line":4,"start":90,"end":91},{"type":"Line","context":"comment","value":"// ...","line":5,"start":94,"end":100},{"type":"T_RCURLY","context":"normal","value":"}","line":6,"start":101,"end":102},{"type":"Line","context":"comment","value":"// $ExpectError","line":8,"start":104,"end":119},{"type":"T_IDENTIFIER","context":"normal","value":"method","line":9,"start":120,"end":126},{"type":"T_LPAREN","context":"normal","value":"(","line":9,"start":126,"end":127},{"type":"T_NUMBER","context":"normal","value":"3.14","line":9,"start":127,"end":131},{"type":"T_RPAREN","context":"normal","value":")","line":9,"start":131,"end":132},{"type":"T_SEMICOLON","context":"normal","value":";","line":9,"start":132,"end":133},{"type":"Line","context":"comment","value":"// Error!","line":9,"start":134,"end":143},{"type":"Line","context":"comment","value":"// $ExpectError","line":10,"start":144,"end":159},{"type":"T_IDENTIFIER","context":"normal","value":"method","line":11,"start":160,"end":166},{"type":"T_LPAREN","context":"normal","value":"(","line":11,"start":166,"end":167},{"type":"T_STRING","context":"normal","value":"'hi'","line":11,"start":167,"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}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot call `method` with `3.14` bound to `value` because number [1] is incompatible with string [2]. [incompatible-call]","context":"method(3.14); // Error!","source":"-","start":{"line":9,"column":8,"offset":127},"end":{"line":9,"column":11,"offset":131}}],"operation":null},{"id":"E2","messages":[{"id":"E2M1","description":"Cannot call `method` with `'hi'` bound to `value` because string [1] is incompatible with number [2]. [incompatible-call]","context":"method('hi'); // Error!","source":"-","start":{"line":11,"column":8,"offset":167},"end":{"line":11,"column":11,"offset":171}}],"operation":null}]}
But you can’t possibly create a value which is both a number and a string,
but you can create a type for it. There’s no practical use for creating types
like this, but it’s a side effect of how intersection types work.