Sometimes it’s useful to create a type which is one of a set of other
types. For example, you might want to write a function which accepts a set of
primitive value types. For this Flow supports union types.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
function toStringPrimitives(value: number | boolean | string) {
return String(value);
}
toStringPrimitives(1);
toStringPrimitives(true);
toStringPrimitives('three');
toStringPrimitives({ prop: 'val' });
toStringPrimitives([1, 2, 3, 4, 5]);
|
Cannot call `toStringPrimitives` with object literal bound to `value` because: [incompatible-call] Either object literal [1] is incompatible with number [2]. Or object literal [1] is incompatible with boolean [3]. Or object literal [1] is incompatible with string [4].
Cannot call `toStringPrimitives` with array literal bound to `value` because: [incompatible-call] Either array literal [1] is incompatible with number [2]. Or array literal [1] is incompatible with boolean [3]. Or array literal [1] is incompatible with string [4].
{"value":"// @flow\nfunction toStringPrimitives(value: number | boolean | string) {\n return String(value);\n}\n\ntoStringPrimitives(1); // Works!\ntoStringPrimitives(true); // Works!\ntoStringPrimitives('three'); // Works!\n\n// $ExpectError\ntoStringPrimitives({ prop: 'val' }); // Error!\n// $ExpectError\ntoStringPrimitives([1, 2, 3, 4, 5]); // 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":"toStringPrimitives","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_NUMBER_TYPE","context":"type","value":"number","line":2,"start":44,"end":50},{"type":"T_BIT_OR","context":"type","value":"|","line":2,"start":51,"end":52},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":2,"start":53,"end":60},{"type":"T_BIT_OR","context":"type","value":"|","line":2,"start":61,"end":62},{"type":"T_STRING_TYPE","context":"type","value":"string","line":2,"start":63,"end":69},{"type":"T_RPAREN","context":"normal","value":")","line":2,"start":69,"end":70},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":71,"end":72},{"type":"T_RETURN","context":"normal","value":"return","line":3,"start":75,"end":81},{"type":"T_IDENTIFIER","context":"normal","value":"String","line":3,"start":82,"end":88},{"type":"T_LPAREN","context":"normal","value":"(","line":3,"start":88,"end":89},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":3,"start":89,"end":94},{"type":"T_RPAREN","context":"normal","value":")","line":3,"start":94,"end":95},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":95,"end":96},{"type":"T_RCURLY","context":"normal","value":"}","line":4,"start":97,"end":98},{"type":"T_IDENTIFIER","context":"normal","value":"toStringPrimitives","line":6,"start":100,"end":118},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":118,"end":119},{"type":"T_NUMBER","context":"normal","value":"1","line":6,"start":119,"end":120},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":120,"end":121},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":121,"end":122},{"type":"Line","context":"comment","value":"// Works!","line":6,"start":129,"end":138},{"type":"T_IDENTIFIER","context":"normal","value":"toStringPrimitives","line":7,"start":139,"end":157},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":157,"end":158},{"type":"T_TRUE","context":"normal","value":"true","line":7,"start":158,"end":162},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":162,"end":163},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":163,"end":164},{"type":"Line","context":"comment","value":"// Works!","line":7,"start":168,"end":177},{"type":"T_IDENTIFIER","context":"normal","value":"toStringPrimitives","line":8,"start":178,"end":196},{"type":"T_LPAREN","context":"normal","value":"(","line":8,"start":196,"end":197},{"type":"T_STRING","context":"normal","value":"'three'","line":8,"start":197,"end":204},{"type":"T_RPAREN","context":"normal","value":")","line":8,"start":204,"end":205},{"type":"T_SEMICOLON","context":"normal","value":";","line":8,"start":205,"end":206},{"type":"Line","context":"comment","value":"// Works!","line":8,"start":207,"end":216},{"type":"Line","context":"comment","value":"// $ExpectError","line":10,"start":218,"end":233},{"type":"T_IDENTIFIER","context":"normal","value":"toStringPrimitives","line":11,"start":234,"end":252},{"type":"T_LPAREN","context":"normal","value":"(","line":11,"start":252,"end":253},{"type":"T_LCURLY","context":"normal","value":"{","line":11,"start":253,"end":254},{"type":"T_IDENTIFIER","context":"normal","value":"prop","line":11,"start":255,"end":259},{"type":"T_COLON","context":"normal","value":":","line":11,"start":259,"end":260},{"type":"T_STRING","context":"normal","value":"'val'","line":11,"start":261,"end":266},{"type":"T_RCURLY","context":"normal","value":"}","line":11,"start":267,"end":268},{"type":"T_RPAREN","context":"normal","value":")","line":11,"start":268,"end":269},{"type":"T_SEMICOLON","context":"normal","value":";","line":11,"start":269,"end":270},{"type":"Line","context":"comment","value":"// Error!","line":11,"start":271,"end":280},{"type":"Line","context":"comment","value":"// $ExpectError","line":12,"start":281,"end":296},{"type":"T_IDENTIFIER","context":"normal","value":"toStringPrimitives","line":13,"start":297,"end":315},{"type":"T_LPAREN","context":"normal","value":"(","line":13,"start":315,"end":316},{"type":"T_LBRACKET","context":"normal","value":"[","line":13,"start":316,"end":317},{"type":"T_NUMBER","context":"normal","value":"1","line":13,"start":317,"end":318},{"type":"T_COMMA","context":"normal","value":",","line":13,"start":318,"end":319},{"type":"T_NUMBER","context":"normal","value":"2","line":13,"start":320,"end":321},{"type":"T_COMMA","context":"normal","value":",","line":13,"start":321,"end":322},{"type":"T_NUMBER","context":"normal","value":"3","line":13,"start":323,"end":324},{"type":"T_COMMA","context":"normal","value":",","line":13,"start":324,"end":325},{"type":"T_NUMBER","context":"normal","value":"4","line":13,"start":326,"end":327},{"type":"T_COMMA","context":"normal","value":",","line":13,"start":327,"end":328},{"type":"T_NUMBER","context":"normal","value":"5","line":13,"start":329,"end":330},{"type":"T_RBRACKET","context":"normal","value":"]","line":13,"start":330,"end":331},{"type":"T_RPAREN","context":"normal","value":")","line":13,"start":331,"end":332},{"type":"T_SEMICOLON","context":"normal","value":";","line":13,"start":332,"end":333},{"type":"Line","context":"comment","value":"// Error!","line":13,"start":334,"end":343}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot call `toStringPrimitives` with object literal bound to `value` because: [incompatible-call] Either object literal [1] is incompatible with number [2]. Or object literal [1] is incompatible with boolean [3]. Or object literal [1] is incompatible with string [4].","context":"toStringPrimitives({ prop: 'val' }); // Error!","source":"-","start":{"line":11,"column":20,"offset":253},"end":{"line":11,"column":34,"offset":268}}],"operation":null},{"id":"E2","messages":[{"id":"E2M1","description":"Cannot call `toStringPrimitives` with array literal bound to `value` because: [incompatible-call] Either array literal [1] is incompatible with number [2]. Or array literal [1] is incompatible with boolean [3]. Or array literal [1] is incompatible with string [4].","context":"toStringPrimitives([1, 2, 3, 4, 5]); // Error!","source":"-","start":{"line":13,"column":20,"offset":316},"end":{"line":13,"column":34,"offset":331}}],"operation":null}]}
Union type syntax
Union types are any number of types which are joined by a vertical bar |
.
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_OR","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_OR","context":"normal","value":"|","line":1,"start":14,"end":15},{"type":"T_ELLIPSIS","context":"normal","value":"...","line":1,"start":16,"end":19},{"type":"T_BIT_OR","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 vertical bar which is useful when breaking union
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_OR","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_OR","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_OR","context":"type","value":"|","line":4,"start":33,"end":34},{"type":"T_ELLIPSIS","context":"normal","value":"...","line":4,"start":35,"end":38},{"type":"T_BIT_OR","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 union type can be any type, even another union type.
1
2
3
4
|
type Numbers = 1 | 2;
type Colors = 'red' | 'blue'
type Fish = Numbers | Colors;
|
{"value":"type Numbers = 1 | 2;\ntype Colors = 'red' | 'blue'\n\ntype Fish = Numbers | Colors;\n","tokens":[{"type":"T_TYPE","context":"normal","value":"type","line":1,"start":0,"end":4},{"type":"T_IDENTIFIER","context":"type","value":"Numbers","line":1,"start":5,"end":12},{"type":"T_ASSIGN","context":"type","value":"=","line":1,"start":13,"end":14},{"type":"T_NUMBER_SINGLETON_TYPE","context":"type","value":"1","line":1,"start":15,"end":16},{"type":"T_BIT_OR","context":"type","value":"|","line":1,"start":17,"end":18},{"type":"T_NUMBER_SINGLETON_TYPE","context":"type","value":"2","line":1,"start":19,"end":20},{"type":"T_SEMICOLON","context":"normal","value":";","line":1,"start":20,"end":21},{"type":"T_TYPE","context":"normal","value":"type","line":2,"start":22,"end":26},{"type":"T_IDENTIFIER","context":"type","value":"Colors","line":2,"start":27,"end":33},{"type":"T_ASSIGN","context":"type","value":"=","line":2,"start":34,"end":35},{"type":"T_STRING","context":"type","value":"'red'","line":2,"start":36,"end":41},{"type":"T_BIT_OR","context":"type","value":"|","line":2,"start":42,"end":43},{"type":"T_STRING","context":"type","value":"'blue'","line":2,"start":44,"end":50},{"type":"T_TYPE","context":"normal","value":"type","line":4,"start":52,"end":56},{"type":"T_IDENTIFIER","context":"type","value":"Fish","line":4,"start":57,"end":61},{"type":"T_ASSIGN","context":"type","value":"=","line":4,"start":62,"end":63},{"type":"T_IDENTIFIER","context":"type","value":"Numbers","line":4,"start":64,"end":71},{"type":"T_BIT_OR","context":"type","value":"|","line":4,"start":72,"end":73},{"type":"T_IDENTIFIER","context":"type","value":"Colors","line":4,"start":74,"end":80},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":80,"end":81}],"errors":[]}
Union types requires one in, but all out
When calling our function that accepts a union type we must pass in one of
those types. But inside of our function we are required to handle all of
the possible types.
Let’s rewrite our function to handle each type individually.
1
2
3
4
5
6
7
8
9
|
function toStringPrimitives(value: number | boolean | string): string {
if (typeof value === 'number') {
return String(value);
} else if (typeof value === 'boolean') {
return String(value);
}
}
|
Cannot expect string as the return type of function because string [1] is incompatible with implicitly-returned undefined. [incompatible-return]
{"value":"// @flow\n// $ExpectError\nfunction toStringPrimitives(value: number | boolean | string): string { // Error!\n if (typeof value === 'number') {\n return String(value);\n } else if (typeof value === 'boolean') {\n return String(value);\n }\n}\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"Line","context":"comment","value":"// $ExpectError","line":2,"start":9,"end":24},{"type":"T_FUNCTION","context":"normal","value":"function","line":3,"start":25,"end":33},{"type":"T_IDENTIFIER","context":"normal","value":"toStringPrimitives","line":3,"start":34,"end":52},{"type":"T_LPAREN","context":"normal","value":"(","line":3,"start":52,"end":53},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":3,"start":53,"end":58},{"type":"T_COLON","context":"type","value":":","line":3,"start":58,"end":59},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":3,"start":60,"end":66},{"type":"T_BIT_OR","context":"type","value":"|","line":3,"start":67,"end":68},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":3,"start":69,"end":76},{"type":"T_BIT_OR","context":"type","value":"|","line":3,"start":77,"end":78},{"type":"T_STRING_TYPE","context":"type","value":"string","line":3,"start":79,"end":85},{"type":"T_RPAREN","context":"normal","value":")","line":3,"start":85,"end":86},{"type":"T_COLON","context":"type","value":":","line":3,"start":86,"end":87},{"type":"T_STRING_TYPE","context":"type","value":"string","line":3,"start":88,"end":94},{"type":"T_LCURLY","context":"normal","value":"{","line":3,"start":95,"end":96},{"type":"Line","context":"comment","value":"// Error!","line":3,"start":97,"end":106},{"type":"T_IF","context":"normal","value":"if","line":4,"start":109,"end":111},{"type":"T_LPAREN","context":"normal","value":"(","line":4,"start":112,"end":113},{"type":"T_TYPEOF","context":"normal","value":"typeof","line":4,"start":113,"end":119},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":4,"start":120,"end":125},{"type":"T_STRICT_EQUAL","context":"normal","value":"===","line":4,"start":126,"end":129},{"type":"T_STRING","context":"normal","value":"'number'","line":4,"start":130,"end":138},{"type":"T_RPAREN","context":"normal","value":")","line":4,"start":138,"end":139},{"type":"T_LCURLY","context":"normal","value":"{","line":4,"start":140,"end":141},{"type":"T_RETURN","context":"normal","value":"return","line":5,"start":146,"end":152},{"type":"T_IDENTIFIER","context":"normal","value":"String","line":5,"start":153,"end":159},{"type":"T_LPAREN","context":"normal","value":"(","line":5,"start":159,"end":160},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":5,"start":160,"end":165},{"type":"T_RPAREN","context":"normal","value":")","line":5,"start":165,"end":166},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":166,"end":167},{"type":"T_RCURLY","context":"normal","value":"}","line":6,"start":170,"end":171},{"type":"T_ELSE","context":"normal","value":"else","line":6,"start":172,"end":176},{"type":"T_IF","context":"normal","value":"if","line":6,"start":177,"end":179},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":180,"end":181},{"type":"T_TYPEOF","context":"normal","value":"typeof","line":6,"start":181,"end":187},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":6,"start":188,"end":193},{"type":"T_STRICT_EQUAL","context":"normal","value":"===","line":6,"start":194,"end":197},{"type":"T_STRING","context":"normal","value":"'boolean'","line":6,"start":198,"end":207},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":207,"end":208},{"type":"T_LCURLY","context":"normal","value":"{","line":6,"start":209,"end":210},{"type":"T_RETURN","context":"normal","value":"return","line":7,"start":215,"end":221},{"type":"T_IDENTIFIER","context":"normal","value":"String","line":7,"start":222,"end":228},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":228,"end":229},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":7,"start":229,"end":234},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":234,"end":235},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":235,"end":236},{"type":"T_RCURLY","context":"normal","value":"}","line":8,"start":239,"end":240},{"type":"T_RCURLY","context":"normal","value":"}","line":9,"start":241,"end":242}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot expect string as the return type of function because string [1] is incompatible with implicitly-returned undefined. [incompatible-return]","context":"function toStringPrimitives(value: number | boolean | string): string { // Error!","source":"-","start":{"line":3,"column":64,"offset":88},"end":{"line":3,"column":69,"offset":94}}],"operation":null}]}
You’ll notice that if we do not handle each possible type of our value, Flow
will give us an error.
Unions & Refinements
When you have a value which is a union type it’s often useful to break it apart
and handle each individual type separately. With union types in Flow you can
“refine” the value down to a single type.
For example, if we have a value with a union type that is a number
, a
boolean
, or a string
, we can treat the number case separately by using
JavaScript’s typeof
operator.
1
2
3
4
5
6
7
|
function toStringPrimitives(value: number | boolean | string) {
if (typeof value === 'number') {
return value.toLocaleString([], { maximumSignificantDigits: 3 });
}
}
|
{"value":"// @flow\nfunction toStringPrimitives(value: number | boolean | string) {\n if (typeof value === 'number') {\n return value.toLocaleString([], { maximumSignificantDigits: 3 }); // Works!\n }\n // ...\n}\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_FUNCTION","context":"normal","value":"function","line":2,"start":9,"end":17},{"type":"T_IDENTIFIER","context":"normal","value":"toStringPrimitives","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_NUMBER_TYPE","context":"type","value":"number","line":2,"start":44,"end":50},{"type":"T_BIT_OR","context":"type","value":"|","line":2,"start":51,"end":52},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":2,"start":53,"end":60},{"type":"T_BIT_OR","context":"type","value":"|","line":2,"start":61,"end":62},{"type":"T_STRING_TYPE","context":"type","value":"string","line":2,"start":63,"end":69},{"type":"T_RPAREN","context":"normal","value":")","line":2,"start":69,"end":70},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":71,"end":72},{"type":"T_IF","context":"normal","value":"if","line":3,"start":75,"end":77},{"type":"T_LPAREN","context":"normal","value":"(","line":3,"start":78,"end":79},{"type":"T_TYPEOF","context":"normal","value":"typeof","line":3,"start":79,"end":85},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":3,"start":86,"end":91},{"type":"T_STRICT_EQUAL","context":"normal","value":"===","line":3,"start":92,"end":95},{"type":"T_STRING","context":"normal","value":"'number'","line":3,"start":96,"end":104},{"type":"T_RPAREN","context":"normal","value":")","line":3,"start":104,"end":105},{"type":"T_LCURLY","context":"normal","value":"{","line":3,"start":106,"end":107},{"type":"T_RETURN","context":"normal","value":"return","line":4,"start":112,"end":118},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":4,"start":119,"end":124},{"type":"T_PERIOD","context":"normal","value":".","line":4,"start":124,"end":125},{"type":"T_IDENTIFIER","context":"normal","value":"toLocaleString","line":4,"start":125,"end":139},{"type":"T_LPAREN","context":"normal","value":"(","line":4,"start":139,"end":140},{"type":"T_LBRACKET","context":"normal","value":"[","line":4,"start":140,"end":141},{"type":"T_RBRACKET","context":"normal","value":"]","line":4,"start":141,"end":142},{"type":"T_COMMA","context":"normal","value":",","line":4,"start":142,"end":143},{"type":"T_LCURLY","context":"normal","value":"{","line":4,"start":144,"end":145},{"type":"T_IDENTIFIER","context":"normal","value":"maximumSignificantDigits","line":4,"start":146,"end":170},{"type":"T_COLON","context":"normal","value":":","line":4,"start":170,"end":171},{"type":"T_NUMBER","context":"normal","value":"3","line":4,"start":172,"end":173},{"type":"T_RCURLY","context":"normal","value":"}","line":4,"start":174,"end":175},{"type":"T_RPAREN","context":"normal","value":")","line":4,"start":175,"end":176},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":176,"end":177},{"type":"Line","context":"comment","value":"// Works!","line":4,"start":178,"end":187},{"type":"T_RCURLY","context":"normal","value":"}","line":5,"start":190,"end":191},{"type":"Line","context":"comment","value":"// ...","line":6,"start":194,"end":200},{"type":"T_RCURLY","context":"normal","value":"}","line":7,"start":201,"end":202}],"errors":[]}
By checking the typeof our value and testing to see if it is a number, Flow
knows that inside of that block it is only a number. We can then write code
which treats our value as a number inside of that block.
Disjoint Unions
There’s a special type of union in Flow known as a “disjoint union” which can
be used in refinements. These disjoint unions are
made up of any number of object types which are each tagged by a single
property.
For example, imagine we have a function for handling a response from a server
after we’ve sent it a request. When the request is successful, we’ll get back
an object with a success
property which is true
and a value
that we’ve
updated.
1
|
{ success: true, value: false };
|
{"value":"{ success: true, value: false };\n","tokens":[{"type":"T_LCURLY","context":"normal","value":"{","line":1,"start":0,"end":1},{"type":"T_IDENTIFIER","context":"normal","value":"success","line":1,"start":2,"end":9},{"type":"T_COLON","context":"normal","value":":","line":1,"start":9,"end":10},{"type":"T_TRUE","context":"normal","value":"true","line":1,"start":11,"end":15},{"type":"T_COMMA","context":"normal","value":",","line":1,"start":15,"end":16},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":1,"start":17,"end":22},{"type":"T_COLON","context":"normal","value":":","line":1,"start":22,"end":23},{"type":"T_FALSE","context":"normal","value":"false","line":1,"start":24,"end":29},{"type":"T_RCURLY","context":"normal","value":"}","line":1,"start":30,"end":31},{"type":"T_SEMICOLON","context":"normal","value":";","line":1,"start":31,"end":32}],"errors":[]}
When the request fails, we’ll get back an object with success
set to false
and an error
property describing the error.
1
|
{ success: false, error: 'Bad request' };
|
{"value":"{ success: false, error: 'Bad request' };\n","tokens":[{"type":"T_LCURLY","context":"normal","value":"{","line":1,"start":0,"end":1},{"type":"T_IDENTIFIER","context":"normal","value":"success","line":1,"start":2,"end":9},{"type":"T_COLON","context":"normal","value":":","line":1,"start":9,"end":10},{"type":"T_FALSE","context":"normal","value":"false","line":1,"start":11,"end":16},{"type":"T_COMMA","context":"normal","value":",","line":1,"start":16,"end":17},{"type":"T_IDENTIFIER","context":"normal","value":"error","line":1,"start":18,"end":23},{"type":"T_COLON","context":"normal","value":":","line":1,"start":23,"end":24},{"type":"T_STRING","context":"normal","value":"'Bad request'","line":1,"start":25,"end":38},{"type":"T_RCURLY","context":"normal","value":"}","line":1,"start":39,"end":40},{"type":"T_SEMICOLON","context":"normal","value":";","line":1,"start":40,"end":41}],"errors":[]}
We can try to express both of these objects in a single object type. However,
we’ll quickly run into issues where we know a property exists based on the
success property but Flow does not.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
type Response = {
success: boolean,
value?: boolean,
error?: string
};
function handleResponse(response: Response) {
if (response.success) {
var value: boolean = response.value;
} else {
var error: string = response.error;
}
}
|
Cannot assign `response.value` to `value` because undefined [1] is incompatible with boolean [2]. [incompatible-type]
Cannot assign `response.error` to `error` because undefined [1] is incompatible with string [2]. [incompatible-type]
{"value":"// @flow\ntype Response = {\n success: boolean,\n value?: boolean,\n error?: string\n};\n\nfunction handleResponse(response: Response) {\n if (response.success) {\n // $ExpectError\n var value: boolean = response.value; // Error!\n } else {\n // $ExpectError\n var error: string = response.error; // Error!\n }\n}\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_TYPE","context":"normal","value":"type","line":2,"start":9,"end":13},{"type":"T_IDENTIFIER","context":"type","value":"Response","line":2,"start":14,"end":22},{"type":"T_ASSIGN","context":"type","value":"=","line":2,"start":23,"end":24},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":25,"end":26},{"type":"T_IDENTIFIER","context":"normal","value":"success","line":3,"start":29,"end":36},{"type":"T_COLON","context":"type","value":":","line":3,"start":36,"end":37},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":3,"start":38,"end":45},{"type":"T_COMMA","context":"type","value":",","line":3,"start":45,"end":46},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":4,"start":49,"end":54},{"type":"T_PLING","context":"type","value":"?","line":4,"start":54,"end":55},{"type":"T_COLON","context":"type","value":":","line":4,"start":55,"end":56},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":4,"start":57,"end":64},{"type":"T_COMMA","context":"type","value":",","line":4,"start":64,"end":65},{"type":"T_IDENTIFIER","context":"normal","value":"error","line":5,"start":68,"end":73},{"type":"T_PLING","context":"type","value":"?","line":5,"start":73,"end":74},{"type":"T_COLON","context":"type","value":":","line":5,"start":74,"end":75},{"type":"T_STRING_TYPE","context":"type","value":"string","line":5,"start":76,"end":82},{"type":"T_RCURLY","context":"type","value":"}","line":6,"start":83,"end":84},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":84,"end":85},{"type":"T_FUNCTION","context":"normal","value":"function","line":8,"start":87,"end":95},{"type":"T_IDENTIFIER","context":"normal","value":"handleResponse","line":8,"start":96,"end":110},{"type":"T_LPAREN","context":"normal","value":"(","line":8,"start":110,"end":111},{"type":"T_IDENTIFIER","context":"normal","value":"response","line":8,"start":111,"end":119},{"type":"T_COLON","context":"type","value":":","line":8,"start":119,"end":120},{"type":"T_IDENTIFIER","context":"type","value":"Response","line":8,"start":121,"end":129},{"type":"T_RPAREN","context":"normal","value":")","line":8,"start":129,"end":130},{"type":"T_LCURLY","context":"normal","value":"{","line":8,"start":131,"end":132},{"type":"T_IF","context":"normal","value":"if","line":9,"start":135,"end":137},{"type":"T_LPAREN","context":"normal","value":"(","line":9,"start":138,"end":139},{"type":"T_IDENTIFIER","context":"normal","value":"response","line":9,"start":139,"end":147},{"type":"T_PERIOD","context":"normal","value":".","line":9,"start":147,"end":148},{"type":"T_IDENTIFIER","context":"normal","value":"success","line":9,"start":148,"end":155},{"type":"T_RPAREN","context":"normal","value":")","line":9,"start":155,"end":156},{"type":"T_LCURLY","context":"normal","value":"{","line":9,"start":157,"end":158},{"type":"Line","context":"comment","value":"// $ExpectError","line":10,"start":163,"end":178},{"type":"T_VAR","context":"normal","value":"var","line":11,"start":183,"end":186},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":11,"start":187,"end":192},{"type":"T_COLON","context":"type","value":":","line":11,"start":192,"end":193},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":11,"start":194,"end":201},{"type":"T_ASSIGN","context":"normal","value":"=","line":11,"start":202,"end":203},{"type":"T_IDENTIFIER","context":"normal","value":"response","line":11,"start":204,"end":212},{"type":"T_PERIOD","context":"normal","value":".","line":11,"start":212,"end":213},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":11,"start":213,"end":218},{"type":"T_SEMICOLON","context":"normal","value":";","line":11,"start":218,"end":219},{"type":"Line","context":"comment","value":"// Error!","line":11,"start":220,"end":229},{"type":"T_RCURLY","context":"normal","value":"}","line":12,"start":232,"end":233},{"type":"T_ELSE","context":"normal","value":"else","line":12,"start":234,"end":238},{"type":"T_LCURLY","context":"normal","value":"{","line":12,"start":239,"end":240},{"type":"Line","context":"comment","value":"// $ExpectError","line":13,"start":245,"end":260},{"type":"T_VAR","context":"normal","value":"var","line":14,"start":265,"end":268},{"type":"T_IDENTIFIER","context":"normal","value":"error","line":14,"start":269,"end":274},{"type":"T_COLON","context":"type","value":":","line":14,"start":274,"end":275},{"type":"T_STRING_TYPE","context":"type","value":"string","line":14,"start":276,"end":282},{"type":"T_ASSIGN","context":"normal","value":"=","line":14,"start":283,"end":284},{"type":"T_IDENTIFIER","context":"normal","value":"response","line":14,"start":285,"end":293},{"type":"T_PERIOD","context":"normal","value":".","line":14,"start":293,"end":294},{"type":"T_IDENTIFIER","context":"normal","value":"error","line":14,"start":294,"end":299},{"type":"T_SEMICOLON","context":"normal","value":";","line":14,"start":299,"end":300},{"type":"Line","context":"comment","value":"// Error!","line":14,"start":301,"end":310},{"type":"T_RCURLY","context":"normal","value":"}","line":15,"start":313,"end":314},{"type":"T_RCURLY","context":"normal","value":"}","line":16,"start":315,"end":316}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot assign `response.value` to `value` because undefined [1] is incompatible with boolean [2]. [incompatible-type]","context":" var value: boolean = response.value; // Error!","source":"-","start":{"line":11,"column":26,"offset":204},"end":{"line":11,"column":39,"offset":218}}],"operation":null},{"id":"E2","messages":[{"id":"E2M1","description":"Cannot assign `response.error` to `error` because undefined [1] is incompatible with string [2]. [incompatible-type]","context":" var error: string = response.error; // Error!","source":"-","start":{"line":14,"column":25,"offset":285},"end":{"line":14,"column":38,"offset":299}}],"operation":null}]}
Trying to combine these two separate types into a single one will only cause us
trouble.
Instead, if we create a union type of both object types, Flow will be able to
know which object we’re using based on the success property.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
type Success = { success: true, value: boolean };
type Failed = { success: false, error: string };
type Response = Success | Failed;
function handleResponse(response: Response) {
if (response.success) {
var value: boolean = response.value;
} else {
var error: string = response.error;
}
}
|
{"value":"// @flow\ntype Success = { success: true, value: boolean };\ntype Failed = { success: false, error: string };\n\ntype Response = Success | Failed;\n\nfunction handleResponse(response: Response) {\n if (response.success) {\n var value: boolean = response.value; // Works!\n } else {\n var error: string = response.error; // Works!\n }\n}\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_TYPE","context":"normal","value":"type","line":2,"start":9,"end":13},{"type":"T_IDENTIFIER","context":"type","value":"Success","line":2,"start":14,"end":21},{"type":"T_ASSIGN","context":"type","value":"=","line":2,"start":22,"end":23},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":24,"end":25},{"type":"T_IDENTIFIER","context":"normal","value":"success","line":2,"start":26,"end":33},{"type":"T_COLON","context":"type","value":":","line":2,"start":33,"end":34},{"type":"T_TRUE","context":"type","value":"true","line":2,"start":35,"end":39},{"type":"T_COMMA","context":"type","value":",","line":2,"start":39,"end":40},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":2,"start":41,"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_TYPE","context":"normal","value":"type","line":3,"start":59,"end":63},{"type":"T_IDENTIFIER","context":"type","value":"Failed","line":3,"start":64,"end":70},{"type":"T_ASSIGN","context":"type","value":"=","line":3,"start":72,"end":73},{"type":"T_LCURLY","context":"type","value":"{","line":3,"start":74,"end":75},{"type":"T_IDENTIFIER","context":"normal","value":"success","line":3,"start":76,"end":83},{"type":"T_COLON","context":"type","value":":","line":3,"start":83,"end":84},{"type":"T_FALSE","context":"type","value":"false","line":3,"start":85,"end":90},{"type":"T_COMMA","context":"type","value":",","line":3,"start":90,"end":91},{"type":"T_IDENTIFIER","context":"normal","value":"error","line":3,"start":92,"end":97},{"type":"T_COLON","context":"type","value":":","line":3,"start":97,"end":98},{"type":"T_STRING_TYPE","context":"type","value":"string","line":3,"start":99,"end":105},{"type":"T_RCURLY","context":"type","value":"}","line":3,"start":106,"end":107},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":107,"end":108},{"type":"T_TYPE","context":"normal","value":"type","line":5,"start":110,"end":114},{"type":"T_IDENTIFIER","context":"type","value":"Response","line":5,"start":115,"end":123},{"type":"T_ASSIGN","context":"type","value":"=","line":5,"start":124,"end":125},{"type":"T_IDENTIFIER","context":"type","value":"Success","line":5,"start":126,"end":133},{"type":"T_BIT_OR","context":"type","value":"|","line":5,"start":134,"end":135},{"type":"T_IDENTIFIER","context":"type","value":"Failed","line":5,"start":136,"end":142},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":142,"end":143},{"type":"T_FUNCTION","context":"normal","value":"function","line":7,"start":145,"end":153},{"type":"T_IDENTIFIER","context":"normal","value":"handleResponse","line":7,"start":154,"end":168},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":168,"end":169},{"type":"T_IDENTIFIER","context":"normal","value":"response","line":7,"start":169,"end":177},{"type":"T_COLON","context":"type","value":":","line":7,"start":177,"end":178},{"type":"T_IDENTIFIER","context":"type","value":"Response","line":7,"start":179,"end":187},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":187,"end":188},{"type":"T_LCURLY","context":"normal","value":"{","line":7,"start":189,"end":190},{"type":"T_IF","context":"normal","value":"if","line":8,"start":193,"end":195},{"type":"T_LPAREN","context":"normal","value":"(","line":8,"start":196,"end":197},{"type":"T_IDENTIFIER","context":"normal","value":"response","line":8,"start":197,"end":205},{"type":"T_PERIOD","context":"normal","value":".","line":8,"start":205,"end":206},{"type":"T_IDENTIFIER","context":"normal","value":"success","line":8,"start":206,"end":213},{"type":"T_RPAREN","context":"normal","value":")","line":8,"start":213,"end":214},{"type":"T_LCURLY","context":"normal","value":"{","line":8,"start":215,"end":216},{"type":"T_VAR","context":"normal","value":"var","line":9,"start":221,"end":224},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":9,"start":225,"end":230},{"type":"T_COLON","context":"type","value":":","line":9,"start":230,"end":231},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":9,"start":232,"end":239},{"type":"T_ASSIGN","context":"normal","value":"=","line":9,"start":240,"end":241},{"type":"T_IDENTIFIER","context":"normal","value":"response","line":9,"start":242,"end":250},{"type":"T_PERIOD","context":"normal","value":".","line":9,"start":250,"end":251},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":9,"start":251,"end":256},{"type":"T_SEMICOLON","context":"normal","value":";","line":9,"start":256,"end":257},{"type":"Line","context":"comment","value":"// Works!","line":9,"start":258,"end":267},{"type":"T_RCURLY","context":"normal","value":"}","line":10,"start":270,"end":271},{"type":"T_ELSE","context":"normal","value":"else","line":10,"start":272,"end":276},{"type":"T_LCURLY","context":"normal","value":"{","line":10,"start":277,"end":278},{"type":"T_VAR","context":"normal","value":"var","line":11,"start":283,"end":286},{"type":"T_IDENTIFIER","context":"normal","value":"error","line":11,"start":287,"end":292},{"type":"T_COLON","context":"type","value":":","line":11,"start":292,"end":293},{"type":"T_STRING_TYPE","context":"type","value":"string","line":11,"start":294,"end":300},{"type":"T_ASSIGN","context":"normal","value":"=","line":11,"start":301,"end":302},{"type":"T_IDENTIFIER","context":"normal","value":"response","line":11,"start":303,"end":311},{"type":"T_PERIOD","context":"normal","value":".","line":11,"start":311,"end":312},{"type":"T_IDENTIFIER","context":"normal","value":"error","line":11,"start":312,"end":317},{"type":"T_SEMICOLON","context":"normal","value":";","line":11,"start":317,"end":318},{"type":"Line","context":"comment","value":"// Works!","line":11,"start":319,"end":328},{"type":"T_RCURLY","context":"normal","value":"}","line":12,"start":331,"end":332},{"type":"T_RCURLY","context":"normal","value":"}","line":13,"start":333,"end":334}],"errors":[]}
Disjoint unions with exact types
Disjoint unions require you to use a single property to distinguish each object
type. You cannot distinguish two different objects by different properties.
1
2
3
4
5
6
7
8
9
10
|
type Success = { success: true, value: boolean };
type Failed = { error: true, message: string };
function handleResponse(response: Success | Failed) {
if (response.success) {
var value: boolean = response.value;
}
}
|
Cannot get `response.value` because property `value` is missing in `Failed` [1]. [prop-missing]
{"value":"// @flow\ntype Success = { success: true, value: boolean };\ntype Failed = { error: true, message: string };\n\nfunction handleResponse(response: Success | Failed) {\n if (response.success) {\n // $ExpectError\n var value: boolean = response.value; // Error!\n }\n}\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_TYPE","context":"normal","value":"type","line":2,"start":9,"end":13},{"type":"T_IDENTIFIER","context":"type","value":"Success","line":2,"start":14,"end":21},{"type":"T_ASSIGN","context":"type","value":"=","line":2,"start":22,"end":23},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":24,"end":25},{"type":"T_IDENTIFIER","context":"normal","value":"success","line":2,"start":26,"end":33},{"type":"T_COLON","context":"type","value":":","line":2,"start":33,"end":34},{"type":"T_TRUE","context":"type","value":"true","line":2,"start":35,"end":39},{"type":"T_COMMA","context":"type","value":",","line":2,"start":39,"end":40},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":2,"start":41,"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_TYPE","context":"normal","value":"type","line":3,"start":59,"end":63},{"type":"T_IDENTIFIER","context":"type","value":"Failed","line":3,"start":64,"end":70},{"type":"T_ASSIGN","context":"type","value":"=","line":3,"start":72,"end":73},{"type":"T_LCURLY","context":"type","value":"{","line":3,"start":74,"end":75},{"type":"T_IDENTIFIER","context":"normal","value":"error","line":3,"start":76,"end":81},{"type":"T_COLON","context":"type","value":":","line":3,"start":81,"end":82},{"type":"T_TRUE","context":"type","value":"true","line":3,"start":83,"end":87},{"type":"T_COMMA","context":"type","value":",","line":3,"start":87,"end":88},{"type":"T_IDENTIFIER","context":"normal","value":"message","line":3,"start":89,"end":96},{"type":"T_COLON","context":"type","value":":","line":3,"start":96,"end":97},{"type":"T_STRING_TYPE","context":"type","value":"string","line":3,"start":98,"end":104},{"type":"T_RCURLY","context":"type","value":"}","line":3,"start":105,"end":106},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":106,"end":107},{"type":"T_FUNCTION","context":"normal","value":"function","line":5,"start":109,"end":117},{"type":"T_IDENTIFIER","context":"normal","value":"handleResponse","line":5,"start":118,"end":132},{"type":"T_LPAREN","context":"normal","value":"(","line":5,"start":132,"end":133},{"type":"T_IDENTIFIER","context":"normal","value":"response","line":5,"start":133,"end":141},{"type":"T_COLON","context":"type","value":":","line":5,"start":141,"end":142},{"type":"T_IDENTIFIER","context":"type","value":"Success","line":5,"start":144,"end":151},{"type":"T_BIT_OR","context":"type","value":"|","line":5,"start":152,"end":153},{"type":"T_IDENTIFIER","context":"type","value":"Failed","line":5,"start":154,"end":160},{"type":"T_RPAREN","context":"normal","value":")","line":5,"start":160,"end":161},{"type":"T_LCURLY","context":"normal","value":"{","line":5,"start":162,"end":163},{"type":"T_IF","context":"normal","value":"if","line":6,"start":166,"end":168},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":169,"end":170},{"type":"T_IDENTIFIER","context":"normal","value":"response","line":6,"start":170,"end":178},{"type":"T_PERIOD","context":"normal","value":".","line":6,"start":178,"end":179},{"type":"T_IDENTIFIER","context":"normal","value":"success","line":6,"start":179,"end":186},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":186,"end":187},{"type":"T_LCURLY","context":"normal","value":"{","line":6,"start":188,"end":189},{"type":"Line","context":"comment","value":"// $ExpectError","line":7,"start":194,"end":209},{"type":"T_VAR","context":"normal","value":"var","line":8,"start":214,"end":217},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":8,"start":218,"end":223},{"type":"T_COLON","context":"type","value":":","line":8,"start":223,"end":224},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":8,"start":225,"end":232},{"type":"T_ASSIGN","context":"normal","value":"=","line":8,"start":233,"end":234},{"type":"T_IDENTIFIER","context":"normal","value":"response","line":8,"start":235,"end":243},{"type":"T_PERIOD","context":"normal","value":".","line":8,"start":243,"end":244},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":8,"start":244,"end":249},{"type":"T_SEMICOLON","context":"normal","value":";","line":8,"start":249,"end":250},{"type":"Line","context":"comment","value":"// Error!","line":8,"start":251,"end":260},{"type":"T_RCURLY","context":"normal","value":"}","line":9,"start":263,"end":264},{"type":"T_RCURLY","context":"normal","value":"}","line":10,"start":265,"end":266}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot get `response.value` because property `value` is missing in `Failed` [1]. [prop-missing]","context":" var value: boolean = response.value; // Error!","source":"-","start":{"line":8,"column":35,"offset":244},"end":{"line":8,"column":39,"offset":249}}],"operation":null}]}
This is because in Flow it is okay to pass an object value with more properties
than the object type expects (because of width subtyping).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
type Success = { success: true, value: boolean };
type Failed = { error: true, message: string };
function handleResponse(response: Success | Failed) {
}
handleResponse({
success: true,
error: true,
value: true,
message: 'hi'
});
|
{"value":"// @flow\ntype Success = { success: true, value: boolean };\ntype Failed = { error: true, message: string };\n\nfunction handleResponse(response: Success | Failed) {\n // ...\n}\n\nhandleResponse({\n success: true,\n error: true,\n value: true,\n message: 'hi'\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":"Success","line":2,"start":14,"end":21},{"type":"T_ASSIGN","context":"type","value":"=","line":2,"start":22,"end":23},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":24,"end":25},{"type":"T_IDENTIFIER","context":"normal","value":"success","line":2,"start":26,"end":33},{"type":"T_COLON","context":"type","value":":","line":2,"start":33,"end":34},{"type":"T_TRUE","context":"type","value":"true","line":2,"start":35,"end":39},{"type":"T_COMMA","context":"type","value":",","line":2,"start":39,"end":40},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":2,"start":41,"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_TYPE","context":"normal","value":"type","line":3,"start":59,"end":63},{"type":"T_IDENTIFIER","context":"type","value":"Failed","line":3,"start":64,"end":70},{"type":"T_ASSIGN","context":"type","value":"=","line":3,"start":72,"end":73},{"type":"T_LCURLY","context":"type","value":"{","line":3,"start":74,"end":75},{"type":"T_IDENTIFIER","context":"normal","value":"error","line":3,"start":76,"end":81},{"type":"T_COLON","context":"type","value":":","line":3,"start":81,"end":82},{"type":"T_TRUE","context":"type","value":"true","line":3,"start":83,"end":87},{"type":"T_COMMA","context":"type","value":",","line":3,"start":87,"end":88},{"type":"T_IDENTIFIER","context":"normal","value":"message","line":3,"start":89,"end":96},{"type":"T_COLON","context":"type","value":":","line":3,"start":96,"end":97},{"type":"T_STRING_TYPE","context":"type","value":"string","line":3,"start":98,"end":104},{"type":"T_RCURLY","context":"type","value":"}","line":3,"start":105,"end":106},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":106,"end":107},{"type":"T_FUNCTION","context":"normal","value":"function","line":5,"start":109,"end":117},{"type":"T_IDENTIFIER","context":"normal","value":"handleResponse","line":5,"start":118,"end":132},{"type":"T_LPAREN","context":"normal","value":"(","line":5,"start":132,"end":133},{"type":"T_IDENTIFIER","context":"normal","value":"response","line":5,"start":133,"end":141},{"type":"T_COLON","context":"type","value":":","line":5,"start":141,"end":142},{"type":"T_IDENTIFIER","context":"type","value":"Success","line":5,"start":144,"end":151},{"type":"T_BIT_OR","context":"type","value":"|","line":5,"start":152,"end":153},{"type":"T_IDENTIFIER","context":"type","value":"Failed","line":5,"start":154,"end":160},{"type":"T_RPAREN","context":"normal","value":")","line":5,"start":160,"end":161},{"type":"T_LCURLY","context":"normal","value":"{","line":5,"start":162,"end":163},{"type":"Line","context":"comment","value":"// ...","line":6,"start":166,"end":172},{"type":"T_RCURLY","context":"normal","value":"}","line":7,"start":173,"end":174},{"type":"T_IDENTIFIER","context":"normal","value":"handleResponse","line":9,"start":176,"end":190},{"type":"T_LPAREN","context":"normal","value":"(","line":9,"start":190,"end":191},{"type":"T_LCURLY","context":"normal","value":"{","line":9,"start":191,"end":192},{"type":"T_IDENTIFIER","context":"normal","value":"success","line":10,"start":195,"end":202},{"type":"T_COLON","context":"normal","value":":","line":10,"start":202,"end":203},{"type":"T_TRUE","context":"normal","value":"true","line":10,"start":204,"end":208},{"type":"T_COMMA","context":"normal","value":",","line":10,"start":208,"end":209},{"type":"T_IDENTIFIER","context":"normal","value":"error","line":11,"start":212,"end":217},{"type":"T_COLON","context":"normal","value":":","line":11,"start":217,"end":218},{"type":"T_TRUE","context":"normal","value":"true","line":11,"start":219,"end":223},{"type":"T_COMMA","context":"normal","value":",","line":11,"start":223,"end":224},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":12,"start":227,"end":232},{"type":"T_COLON","context":"normal","value":":","line":12,"start":232,"end":233},{"type":"T_TRUE","context":"normal","value":"true","line":12,"start":234,"end":238},{"type":"T_COMMA","context":"normal","value":",","line":12,"start":238,"end":239},{"type":"T_IDENTIFIER","context":"normal","value":"message","line":13,"start":242,"end":249},{"type":"T_COLON","context":"normal","value":":","line":13,"start":249,"end":250},{"type":"T_STRING","context":"normal","value":"'hi'","line":13,"start":251,"end":255},{"type":"T_RCURLY","context":"normal","value":"}","line":14,"start":256,"end":257},{"type":"T_RPAREN","context":"normal","value":")","line":14,"start":257,"end":258},{"type":"T_SEMICOLON","context":"normal","value":";","line":14,"start":258,"end":259}],"errors":[]}
Unless the objects somehow conflict with one another there is no way to
distinguish them.
However, to get around this you could use exact object types.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
type Success = {| success: true, value: boolean |};
type Failed = {| error: true, message: string |};
type Response = Success | Failed;
function handleResponse(response: Response) {
if (response.success) {
var value: boolean = response.value;
} else {
var message: string = response.message;
}
}
|
{"value":"// @flow\ntype Success = {| success: true, value: boolean |};\ntype Failed = {| error: true, message: string |};\n\ntype Response = Success | Failed;\n\nfunction handleResponse(response: Response) {\n if (response.success) {\n var value: boolean = response.value;\n } else {\n var message: string = response.message;\n }\n}\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_TYPE","context":"normal","value":"type","line":2,"start":9,"end":13},{"type":"T_IDENTIFIER","context":"type","value":"Success","line":2,"start":14,"end":21},{"type":"T_ASSIGN","context":"type","value":"=","line":2,"start":22,"end":23},{"type":"T_LCURLYBAR","context":"type","value":"{|","line":2,"start":24,"end":26},{"type":"T_IDENTIFIER","context":"normal","value":"success","line":2,"start":27,"end":34},{"type":"T_COLON","context":"type","value":":","line":2,"start":34,"end":35},{"type":"T_TRUE","context":"type","value":"true","line":2,"start":36,"end":40},{"type":"T_COMMA","context":"type","value":",","line":2,"start":40,"end":41},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":2,"start":42,"end":47},{"type":"T_COLON","context":"type","value":":","line":2,"start":47,"end":48},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":2,"start":49,"end":56},{"type":"T_RCURLYBAR","context":"type","value":"|}","line":2,"start":57,"end":59},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":59,"end":60},{"type":"T_TYPE","context":"normal","value":"type","line":3,"start":61,"end":65},{"type":"T_IDENTIFIER","context":"type","value":"Failed","line":3,"start":66,"end":72},{"type":"T_ASSIGN","context":"type","value":"=","line":3,"start":74,"end":75},{"type":"T_LCURLYBAR","context":"type","value":"{|","line":3,"start":76,"end":78},{"type":"T_IDENTIFIER","context":"normal","value":"error","line":3,"start":79,"end":84},{"type":"T_COLON","context":"type","value":":","line":3,"start":84,"end":85},{"type":"T_TRUE","context":"type","value":"true","line":3,"start":86,"end":90},{"type":"T_COMMA","context":"type","value":",","line":3,"start":90,"end":91},{"type":"T_IDENTIFIER","context":"normal","value":"message","line":3,"start":92,"end":99},{"type":"T_COLON","context":"type","value":":","line":3,"start":99,"end":100},{"type":"T_STRING_TYPE","context":"type","value":"string","line":3,"start":101,"end":107},{"type":"T_RCURLYBAR","context":"type","value":"|}","line":3,"start":108,"end":110},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":110,"end":111},{"type":"T_TYPE","context":"normal","value":"type","line":5,"start":113,"end":117},{"type":"T_IDENTIFIER","context":"type","value":"Response","line":5,"start":118,"end":126},{"type":"T_ASSIGN","context":"type","value":"=","line":5,"start":127,"end":128},{"type":"T_IDENTIFIER","context":"type","value":"Success","line":5,"start":129,"end":136},{"type":"T_BIT_OR","context":"type","value":"|","line":5,"start":137,"end":138},{"type":"T_IDENTIFIER","context":"type","value":"Failed","line":5,"start":139,"end":145},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":145,"end":146},{"type":"T_FUNCTION","context":"normal","value":"function","line":7,"start":148,"end":156},{"type":"T_IDENTIFIER","context":"normal","value":"handleResponse","line":7,"start":157,"end":171},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":171,"end":172},{"type":"T_IDENTIFIER","context":"normal","value":"response","line":7,"start":172,"end":180},{"type":"T_COLON","context":"type","value":":","line":7,"start":180,"end":181},{"type":"T_IDENTIFIER","context":"type","value":"Response","line":7,"start":182,"end":190},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":190,"end":191},{"type":"T_LCURLY","context":"normal","value":"{","line":7,"start":192,"end":193},{"type":"T_IF","context":"normal","value":"if","line":8,"start":196,"end":198},{"type":"T_LPAREN","context":"normal","value":"(","line":8,"start":199,"end":200},{"type":"T_IDENTIFIER","context":"normal","value":"response","line":8,"start":200,"end":208},{"type":"T_PERIOD","context":"normal","value":".","line":8,"start":208,"end":209},{"type":"T_IDENTIFIER","context":"normal","value":"success","line":8,"start":209,"end":216},{"type":"T_RPAREN","context":"normal","value":")","line":8,"start":216,"end":217},{"type":"T_LCURLY","context":"normal","value":"{","line":8,"start":218,"end":219},{"type":"T_VAR","context":"normal","value":"var","line":9,"start":224,"end":227},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":9,"start":228,"end":233},{"type":"T_COLON","context":"type","value":":","line":9,"start":233,"end":234},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":9,"start":235,"end":242},{"type":"T_ASSIGN","context":"normal","value":"=","line":9,"start":243,"end":244},{"type":"T_IDENTIFIER","context":"normal","value":"response","line":9,"start":245,"end":253},{"type":"T_PERIOD","context":"normal","value":".","line":9,"start":253,"end":254},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":9,"start":254,"end":259},{"type":"T_SEMICOLON","context":"normal","value":";","line":9,"start":259,"end":260},{"type":"T_RCURLY","context":"normal","value":"}","line":10,"start":263,"end":264},{"type":"T_ELSE","context":"normal","value":"else","line":10,"start":265,"end":269},{"type":"T_LCURLY","context":"normal","value":"{","line":10,"start":270,"end":271},{"type":"T_VAR","context":"normal","value":"var","line":11,"start":276,"end":279},{"type":"T_IDENTIFIER","context":"normal","value":"message","line":11,"start":280,"end":287},{"type":"T_COLON","context":"type","value":":","line":11,"start":287,"end":288},{"type":"T_STRING_TYPE","context":"type","value":"string","line":11,"start":289,"end":295},{"type":"T_ASSIGN","context":"normal","value":"=","line":11,"start":296,"end":297},{"type":"T_IDENTIFIER","context":"normal","value":"response","line":11,"start":298,"end":306},{"type":"T_PERIOD","context":"normal","value":".","line":11,"start":306,"end":307},{"type":"T_IDENTIFIER","context":"normal","value":"message","line":11,"start":307,"end":314},{"type":"T_SEMICOLON","context":"normal","value":";","line":11,"start":314,"end":315},{"type":"T_RCURLY","context":"normal","value":"}","line":12,"start":318,"end":319},{"type":"T_RCURLY","context":"normal","value":"}","line":13,"start":320,"end":321}],"errors":[]}
With exact object types, we cannot have additional properties, so the objects
conflict with one another and we are able to distinguish which is which.