Sometimes it is useful to assert a type without using something like a function
or a variable to do so. For this Flow supports an inline type cast expression
syntax which can be used in a number of different ways.
Type Cast Expression Syntax
In order to create a type cast expression around a value
, add a colon :
with the Type
and wrap the expression with parentheses (
)
.
{"value":"(value: Type)\n","tokens":[{"type":"T_LPAREN","context":"normal","value":"(","line":1,"start":0,"end":1},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":1,"start":1,"end":6},{"type":"T_COLON","context":"type","value":":","line":1,"start":6,"end":7},{"type":"T_IDENTIFIER","context":"type","value":"Type","line":1,"start":8,"end":12},{"type":"T_RPAREN","context":"normal","value":")","line":1,"start":12,"end":13}],"errors":[]}
Note: The parentheses are necessary to avoid ambiguity with other syntax.
Type cast expressions can appear anywhere an expression can appear.
1
2
3
|
let val = (value: Type);
let obj = { prop: (value: Type) };
let arr = ([(value: Type), (value: Type)]: Array<Type>);
|
{"value":"let val = (value: Type);\nlet obj = { prop: (value: Type) };\nlet arr = ([(value: Type), (value: Type)]: Array<Type>);\n","tokens":[{"type":"T_LET","context":"normal","value":"let","line":1,"start":0,"end":3},{"type":"T_IDENTIFIER","context":"normal","value":"val","line":1,"start":4,"end":7},{"type":"T_ASSIGN","context":"normal","value":"=","line":1,"start":8,"end":9},{"type":"T_LPAREN","context":"normal","value":"(","line":1,"start":10,"end":11},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":1,"start":11,"end":16},{"type":"T_COLON","context":"type","value":":","line":1,"start":16,"end":17},{"type":"T_IDENTIFIER","context":"type","value":"Type","line":1,"start":18,"end":22},{"type":"T_RPAREN","context":"normal","value":")","line":1,"start":22,"end":23},{"type":"T_SEMICOLON","context":"normal","value":";","line":1,"start":23,"end":24},{"type":"T_LET","context":"normal","value":"let","line":2,"start":25,"end":28},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":2,"start":29,"end":32},{"type":"T_ASSIGN","context":"normal","value":"=","line":2,"start":33,"end":34},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":35,"end":36},{"type":"T_IDENTIFIER","context":"normal","value":"prop","line":2,"start":37,"end":41},{"type":"T_COLON","context":"normal","value":":","line":2,"start":41,"end":42},{"type":"T_LPAREN","context":"normal","value":"(","line":2,"start":43,"end":44},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":2,"start":44,"end":49},{"type":"T_COLON","context":"type","value":":","line":2,"start":49,"end":50},{"type":"T_IDENTIFIER","context":"type","value":"Type","line":2,"start":51,"end":55},{"type":"T_RPAREN","context":"normal","value":")","line":2,"start":55,"end":56},{"type":"T_RCURLY","context":"normal","value":"}","line":2,"start":57,"end":58},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":58,"end":59},{"type":"T_LET","context":"normal","value":"let","line":3,"start":60,"end":63},{"type":"T_IDENTIFIER","context":"normal","value":"arr","line":3,"start":64,"end":67},{"type":"T_ASSIGN","context":"normal","value":"=","line":3,"start":68,"end":69},{"type":"T_LPAREN","context":"normal","value":"(","line":3,"start":70,"end":71},{"type":"T_LBRACKET","context":"normal","value":"[","line":3,"start":71,"end":72},{"type":"T_LPAREN","context":"normal","value":"(","line":3,"start":72,"end":73},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":3,"start":73,"end":78},{"type":"T_COLON","context":"type","value":":","line":3,"start":78,"end":79},{"type":"T_IDENTIFIER","context":"type","value":"Type","line":3,"start":80,"end":84},{"type":"T_RPAREN","context":"normal","value":")","line":3,"start":84,"end":85},{"type":"T_COMMA","context":"normal","value":",","line":3,"start":85,"end":86},{"type":"T_LPAREN","context":"normal","value":"(","line":3,"start":87,"end":88},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":3,"start":88,"end":93},{"type":"T_COLON","context":"type","value":":","line":3,"start":93,"end":94},{"type":"T_IDENTIFIER","context":"type","value":"Type","line":3,"start":95,"end":99},{"type":"T_RPAREN","context":"normal","value":")","line":3,"start":99,"end":100},{"type":"T_RBRACKET","context":"normal","value":"]","line":3,"start":100,"end":101},{"type":"T_COLON","context":"type","value":":","line":3,"start":101,"end":102},{"type":"T_IDENTIFIER","context":"type","value":"Array","line":3,"start":103,"end":108},{"type":"T_LESS_THAN","context":"type","value":"<","line":3,"start":108,"end":109},{"type":"T_IDENTIFIER","context":"type","value":"Type","line":3,"start":109,"end":113},{"type":"T_GREATER_THAN","context":"type","value":">","line":3,"start":113,"end":114},{"type":"T_RPAREN","context":"normal","value":")","line":3,"start":114,"end":115},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":115,"end":116}],"errors":[]}
The value itself can also be an expression:
{"value":"(2 + 2: number);\n","tokens":[{"type":"T_LPAREN","context":"normal","value":"(","line":1,"start":0,"end":1},{"type":"T_NUMBER","context":"normal","value":"2","line":1,"start":1,"end":2},{"type":"T_PLUS","context":"normal","value":"+","line":1,"start":3,"end":4},{"type":"T_NUMBER","context":"normal","value":"2","line":1,"start":5,"end":6},{"type":"T_COLON","context":"type","value":":","line":1,"start":6,"end":7},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":1,"start":8,"end":14},{"type":"T_RPAREN","context":"normal","value":")","line":1,"start":14,"end":15},{"type":"T_SEMICOLON","context":"normal","value":";","line":1,"start":15,"end":16}],"errors":[]}
When you strip the types all that is left is the value.
{"value":"(value: Type);\n","tokens":[{"type":"T_LPAREN","context":"normal","value":"(","line":1,"start":0,"end":1},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":1,"start":1,"end":6},{"type":"T_COLON","context":"type","value":":","line":1,"start":6,"end":7},{"type":"T_IDENTIFIER","context":"type","value":"Type","line":1,"start":8,"end":12},{"type":"T_RPAREN","context":"normal","value":")","line":1,"start":12,"end":13},{"type":"T_SEMICOLON","context":"normal","value":";","line":1,"start":13,"end":14}],"errors":[]}
{"value":"value;\n","tokens":[{"type":"T_IDENTIFIER","context":"normal","value":"value","line":1,"start":0,"end":5},{"type":"T_SEMICOLON","context":"normal","value":";","line":1,"start":5,"end":6}],"errors":[]}
Type Assertions
Using type cast expressions you can assert that values are certain types.
1
2
3
4
5
6
|
let value = 42;
(value: 42);
(value: number);
(value: string);
|
Cannot cast `value` to string because number [1] is incompatible with string [2]. [incompatible-cast]
{"value":"// @flow\nlet value = 42;\n\n(value: 42); // Works!\n(value: number); // Works!\n(value: string); // Error!\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_LET","context":"normal","value":"let","line":2,"start":9,"end":12},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":2,"start":13,"end":18},{"type":"T_ASSIGN","context":"normal","value":"=","line":2,"start":19,"end":20},{"type":"T_NUMBER","context":"normal","value":"42","line":2,"start":21,"end":23},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":23,"end":24},{"type":"T_LPAREN","context":"normal","value":"(","line":4,"start":26,"end":27},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":4,"start":27,"end":32},{"type":"T_COLON","context":"type","value":":","line":4,"start":32,"end":33},{"type":"T_NUMBER_SINGLETON_TYPE","context":"type","value":"42","line":4,"start":34,"end":36},{"type":"T_RPAREN","context":"normal","value":")","line":4,"start":36,"end":37},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":37,"end":38},{"type":"Line","context":"comment","value":"// Works!","line":4,"start":43,"end":52},{"type":"T_LPAREN","context":"normal","value":"(","line":5,"start":53,"end":54},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":5,"start":54,"end":59},{"type":"T_COLON","context":"type","value":":","line":5,"start":59,"end":60},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":5,"start":61,"end":67},{"type":"T_RPAREN","context":"normal","value":")","line":5,"start":67,"end":68},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":68,"end":69},{"type":"Line","context":"comment","value":"// Works!","line":5,"start":70,"end":79},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":80,"end":81},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":6,"start":81,"end":86},{"type":"T_COLON","context":"type","value":":","line":6,"start":86,"end":87},{"type":"T_STRING_TYPE","context":"type","value":"string","line":6,"start":88,"end":94},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":94,"end":95},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":95,"end":96},{"type":"Line","context":"comment","value":"// Error!","line":6,"start":97,"end":106}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot cast `value` to string because number [1] is incompatible with string [2]. [incompatible-cast]","context":"(value: string); // Error!","source":"-","start":{"line":6,"column":2,"offset":81},"end":{"line":6,"column":6,"offset":86}}],"operation":null}]}
Asserting types in this way works the same as types do anywhere else.
Type Casting
When you write a type cast expression, the result of that expression is the
value with the provided type. If you hold onto the resulting value, it will
have the new type.
1
2
3
4
5
6
7
8
9
10
11
|
let value = 42;
(value: 42);
(value: number);
let newValue = (value: number);
(newValue: 42);
(newValue: number);
|
Cannot cast `newValue` to number literal `42` because number [1] is incompatible with number literal `42` [2]. [incompatible-cast]
{"value":"// @flow\nlet value = 42;\n\n(value: 42); // Works!\n(value: number); // Works!\n\nlet newValue = (value: number);\n\n// $ExpectError\n(newValue: 42); // Error!\n(newValue: number); // Works!\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_LET","context":"normal","value":"let","line":2,"start":9,"end":12},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":2,"start":13,"end":18},{"type":"T_ASSIGN","context":"normal","value":"=","line":2,"start":19,"end":20},{"type":"T_NUMBER","context":"normal","value":"42","line":2,"start":21,"end":23},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":23,"end":24},{"type":"T_LPAREN","context":"normal","value":"(","line":4,"start":26,"end":27},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":4,"start":27,"end":32},{"type":"T_COLON","context":"type","value":":","line":4,"start":32,"end":33},{"type":"T_NUMBER_SINGLETON_TYPE","context":"type","value":"42","line":4,"start":34,"end":36},{"type":"T_RPAREN","context":"normal","value":")","line":4,"start":36,"end":37},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":37,"end":38},{"type":"Line","context":"comment","value":"// Works!","line":4,"start":43,"end":52},{"type":"T_LPAREN","context":"normal","value":"(","line":5,"start":53,"end":54},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":5,"start":54,"end":59},{"type":"T_COLON","context":"type","value":":","line":5,"start":59,"end":60},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":5,"start":61,"end":67},{"type":"T_RPAREN","context":"normal","value":")","line":5,"start":67,"end":68},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":68,"end":69},{"type":"Line","context":"comment","value":"// Works!","line":5,"start":70,"end":79},{"type":"T_LET","context":"normal","value":"let","line":7,"start":81,"end":84},{"type":"T_IDENTIFIER","context":"normal","value":"newValue","line":7,"start":85,"end":93},{"type":"T_ASSIGN","context":"normal","value":"=","line":7,"start":94,"end":95},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":96,"end":97},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":7,"start":97,"end":102},{"type":"T_COLON","context":"type","value":":","line":7,"start":102,"end":103},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":7,"start":104,"end":110},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":110,"end":111},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":111,"end":112},{"type":"Line","context":"comment","value":"// $ExpectError","line":9,"start":114,"end":129},{"type":"T_LPAREN","context":"normal","value":"(","line":10,"start":130,"end":131},{"type":"T_IDENTIFIER","context":"normal","value":"newValue","line":10,"start":131,"end":139},{"type":"T_COLON","context":"type","value":":","line":10,"start":139,"end":140},{"type":"T_NUMBER_SINGLETON_TYPE","context":"type","value":"42","line":10,"start":141,"end":143},{"type":"T_RPAREN","context":"normal","value":")","line":10,"start":143,"end":144},{"type":"T_SEMICOLON","context":"normal","value":";","line":10,"start":144,"end":145},{"type":"Line","context":"comment","value":"// Error!","line":10,"start":150,"end":159},{"type":"T_LPAREN","context":"normal","value":"(","line":11,"start":160,"end":161},{"type":"T_IDENTIFIER","context":"normal","value":"newValue","line":11,"start":161,"end":169},{"type":"T_COLON","context":"type","value":":","line":11,"start":169,"end":170},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":11,"start":171,"end":177},{"type":"T_RPAREN","context":"normal","value":")","line":11,"start":177,"end":178},{"type":"T_SEMICOLON","context":"normal","value":";","line":11,"start":178,"end":179},{"type":"Line","context":"comment","value":"// Works!","line":11,"start":180,"end":189}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot cast `newValue` to number literal `42` because number [1] is incompatible with number literal `42` [2]. [incompatible-cast]","context":"(newValue: 42); // Error!","source":"-","start":{"line":10,"column":2,"offset":131},"end":{"line":10,"column":9,"offset":139}}],"operation":null}]}
Using type cast expressions
Note: We’re going to go through a stripped down example for
demonstrating how to make use of type cast expressions. This example is not
solved well in practice.
Type Casting through any
Because type casts work the same as all other type annotations, you can only
cast values to less specific types. You cannot change the type or make it
something more specific.
But you can use any to cast to whatever type you want.
1
2
3
4
5
6
7
8
9
10
11
|
let value = 42;
(value: number);
(value: string);
let newValue = ((value: any): string);
(newValue: number);
(newValue: string);
|
{"value":"let value = 42;\n\n(value: number); // Works!\n// $ExpectError\n(value: string); // Error!\n\nlet newValue = ((value: any): string);\n\n// $ExpectError\n(newValue: number); // Error!\n(newValue: string); // Works!\n","tokens":[{"type":"T_LET","context":"normal","value":"let","line":1,"start":0,"end":3},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":1,"start":4,"end":9},{"type":"T_ASSIGN","context":"normal","value":"=","line":1,"start":10,"end":11},{"type":"T_NUMBER","context":"normal","value":"42","line":1,"start":12,"end":14},{"type":"T_SEMICOLON","context":"normal","value":";","line":1,"start":14,"end":15},{"type":"T_LPAREN","context":"normal","value":"(","line":3,"start":17,"end":18},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":3,"start":18,"end":23},{"type":"T_COLON","context":"type","value":":","line":3,"start":23,"end":24},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":3,"start":25,"end":31},{"type":"T_RPAREN","context":"normal","value":")","line":3,"start":31,"end":32},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":32,"end":33},{"type":"Line","context":"comment","value":"// Works!","line":3,"start":34,"end":43},{"type":"Line","context":"comment","value":"// $ExpectError","line":4,"start":44,"end":59},{"type":"T_LPAREN","context":"normal","value":"(","line":5,"start":60,"end":61},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":5,"start":61,"end":66},{"type":"T_COLON","context":"type","value":":","line":5,"start":66,"end":67},{"type":"T_STRING_TYPE","context":"type","value":"string","line":5,"start":68,"end":74},{"type":"T_RPAREN","context":"normal","value":")","line":5,"start":74,"end":75},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":75,"end":76},{"type":"Line","context":"comment","value":"// Error!","line":5,"start":77,"end":86},{"type":"T_LET","context":"normal","value":"let","line":7,"start":88,"end":91},{"type":"T_IDENTIFIER","context":"normal","value":"newValue","line":7,"start":92,"end":100},{"type":"T_ASSIGN","context":"normal","value":"=","line":7,"start":101,"end":102},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":103,"end":104},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":104,"end":105},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":7,"start":105,"end":110},{"type":"T_COLON","context":"type","value":":","line":7,"start":110,"end":111},{"type":"T_ANY_TYPE","context":"type","value":"any","line":7,"start":112,"end":115},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":115,"end":116},{"type":"T_COLON","context":"type","value":":","line":7,"start":116,"end":117},{"type":"T_STRING_TYPE","context":"type","value":"string","line":7,"start":118,"end":124},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":124,"end":125},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":125,"end":126},{"type":"Line","context":"comment","value":"// $ExpectError","line":9,"start":128,"end":143},{"type":"T_LPAREN","context":"normal","value":"(","line":10,"start":144,"end":145},{"type":"T_IDENTIFIER","context":"normal","value":"newValue","line":10,"start":145,"end":153},{"type":"T_COLON","context":"type","value":":","line":10,"start":153,"end":154},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":10,"start":155,"end":161},{"type":"T_RPAREN","context":"normal","value":")","line":10,"start":161,"end":162},{"type":"T_SEMICOLON","context":"normal","value":";","line":10,"start":162,"end":163},{"type":"Line","context":"comment","value":"// Error!","line":10,"start":164,"end":173},{"type":"T_LPAREN","context":"normal","value":"(","line":11,"start":174,"end":175},{"type":"T_IDENTIFIER","context":"normal","value":"newValue","line":11,"start":175,"end":183},{"type":"T_COLON","context":"type","value":":","line":11,"start":183,"end":184},{"type":"T_STRING_TYPE","context":"type","value":"string","line":11,"start":185,"end":191},{"type":"T_RPAREN","context":"normal","value":")","line":11,"start":191,"end":192},{"type":"T_SEMICOLON","context":"normal","value":";","line":11,"start":192,"end":193},{"type":"Line","context":"comment","value":"// Works!","line":11,"start":194,"end":203}],"errors":[]}
By casting the value to any, you can then cast to whatever you want.
This is unsafe and not recommended. But it’s sometimes useful when you are
doing something with a value which is very difficult or impossible to type and
want to make sure that the result has the desired type.
For example, the following function for cloning an object.
1
2
3
4
5
6
7
8
9
|
function cloneObject(obj) {
const clone = {};
Object.keys(obj).forEach(key => {
clone[key] = obj[key];
});
return clone;
}
|
{"value":"function cloneObject(obj) {\n const clone = {};\n\n Object.keys(obj).forEach(key => {\n clone[key] = obj[key];\n });\n\n return clone;\n}\n","tokens":[{"type":"T_FUNCTION","context":"normal","value":"function","line":1,"start":0,"end":8},{"type":"T_IDENTIFIER","context":"normal","value":"cloneObject","line":1,"start":9,"end":20},{"type":"T_LPAREN","context":"normal","value":"(","line":1,"start":20,"end":21},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":1,"start":21,"end":24},{"type":"T_RPAREN","context":"normal","value":")","line":1,"start":24,"end":25},{"type":"T_LCURLY","context":"normal","value":"{","line":1,"start":26,"end":27},{"type":"T_CONST","context":"normal","value":"const","line":2,"start":30,"end":35},{"type":"T_IDENTIFIER","context":"normal","value":"clone","line":2,"start":36,"end":41},{"type":"T_ASSIGN","context":"normal","value":"=","line":2,"start":42,"end":43},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":44,"end":45},{"type":"T_RCURLY","context":"normal","value":"}","line":2,"start":45,"end":46},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":46,"end":47},{"type":"T_IDENTIFIER","context":"normal","value":"Object","line":4,"start":51,"end":57},{"type":"T_PERIOD","context":"normal","value":".","line":4,"start":57,"end":58},{"type":"T_IDENTIFIER","context":"normal","value":"keys","line":4,"start":58,"end":62},{"type":"T_LPAREN","context":"normal","value":"(","line":4,"start":62,"end":63},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":4,"start":63,"end":66},{"type":"T_RPAREN","context":"normal","value":")","line":4,"start":66,"end":67},{"type":"T_PERIOD","context":"normal","value":".","line":4,"start":67,"end":68},{"type":"T_IDENTIFIER","context":"normal","value":"forEach","line":4,"start":68,"end":75},{"type":"T_LPAREN","context":"normal","value":"(","line":4,"start":75,"end":76},{"type":"T_IDENTIFIER","context":"normal","value":"key","line":4,"start":76,"end":79},{"type":"T_ARROW","context":"normal","value":"=>","line":4,"start":80,"end":82},{"type":"T_LCURLY","context":"normal","value":"{","line":4,"start":83,"end":84},{"type":"T_IDENTIFIER","context":"normal","value":"clone","line":5,"start":89,"end":94},{"type":"T_LBRACKET","context":"normal","value":"[","line":5,"start":94,"end":95},{"type":"T_IDENTIFIER","context":"normal","value":"key","line":5,"start":95,"end":98},{"type":"T_RBRACKET","context":"normal","value":"]","line":5,"start":98,"end":99},{"type":"T_ASSIGN","context":"normal","value":"=","line":5,"start":100,"end":101},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":5,"start":102,"end":105},{"type":"T_LBRACKET","context":"normal","value":"[","line":5,"start":105,"end":106},{"type":"T_IDENTIFIER","context":"normal","value":"key","line":5,"start":106,"end":109},{"type":"T_RBRACKET","context":"normal","value":"]","line":5,"start":109,"end":110},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":110,"end":111},{"type":"T_RCURLY","context":"normal","value":"}","line":6,"start":114,"end":115},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":115,"end":116},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":116,"end":117},{"type":"T_RETURN","context":"normal","value":"return","line":8,"start":121,"end":127},{"type":"T_IDENTIFIER","context":"normal","value":"clone","line":8,"start":128,"end":133},{"type":"T_SEMICOLON","context":"normal","value":";","line":8,"start":133,"end":134},{"type":"T_RCURLY","context":"normal","value":"}","line":9,"start":135,"end":136}],"errors":[]}
It would be hard to create a type for this because we’re creating a new object
based on another object.
If we cast through any, we can return a type which is more useful.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
function cloneObject(obj) {
const clone = {};
Object.keys(obj).forEach(key => {
clone[key] = obj[key];
});
return ((clone: any): typeof obj);
}
const clone = cloneObject({
foo: 1,
bar: true,
baz: 'three'
});
(clone.foo: 1);
(clone.bar: true);
(clone.baz: 'three');
|
{"value":"// @flow\nfunction cloneObject(obj) {\n const clone = {};\n\n Object.keys(obj).forEach(key => {\n clone[key] = obj[key];\n });\n\n return ((clone: any): typeof obj); // <<\n}\n\nconst clone = cloneObject({\n foo: 1,\n bar: true,\n baz: 'three'\n});\n\n(clone.foo: 1); // Works!\n(clone.bar: true); // Works!\n(clone.baz: 'three'); // Works!\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_FUNCTION","context":"normal","value":"function","line":2,"start":9,"end":17},{"type":"T_IDENTIFIER","context":"normal","value":"cloneObject","line":2,"start":18,"end":29},{"type":"T_LPAREN","context":"normal","value":"(","line":2,"start":29,"end":30},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":2,"start":30,"end":33},{"type":"T_RPAREN","context":"normal","value":")","line":2,"start":33,"end":34},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":35,"end":36},{"type":"T_CONST","context":"normal","value":"const","line":3,"start":39,"end":44},{"type":"T_IDENTIFIER","context":"normal","value":"clone","line":3,"start":45,"end":50},{"type":"T_ASSIGN","context":"normal","value":"=","line":3,"start":51,"end":52},{"type":"T_LCURLY","context":"normal","value":"{","line":3,"start":53,"end":54},{"type":"T_RCURLY","context":"normal","value":"}","line":3,"start":54,"end":55},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":55,"end":56},{"type":"T_IDENTIFIER","context":"normal","value":"Object","line":5,"start":60,"end":66},{"type":"T_PERIOD","context":"normal","value":".","line":5,"start":66,"end":67},{"type":"T_IDENTIFIER","context":"normal","value":"keys","line":5,"start":67,"end":71},{"type":"T_LPAREN","context":"normal","value":"(","line":5,"start":71,"end":72},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":5,"start":72,"end":75},{"type":"T_RPAREN","context":"normal","value":")","line":5,"start":75,"end":76},{"type":"T_PERIOD","context":"normal","value":".","line":5,"start":76,"end":77},{"type":"T_IDENTIFIER","context":"normal","value":"forEach","line":5,"start":77,"end":84},{"type":"T_LPAREN","context":"normal","value":"(","line":5,"start":84,"end":85},{"type":"T_IDENTIFIER","context":"normal","value":"key","line":5,"start":85,"end":88},{"type":"T_ARROW","context":"normal","value":"=>","line":5,"start":89,"end":91},{"type":"T_LCURLY","context":"normal","value":"{","line":5,"start":92,"end":93},{"type":"T_IDENTIFIER","context":"normal","value":"clone","line":6,"start":98,"end":103},{"type":"T_LBRACKET","context":"normal","value":"[","line":6,"start":103,"end":104},{"type":"T_IDENTIFIER","context":"normal","value":"key","line":6,"start":104,"end":107},{"type":"T_RBRACKET","context":"normal","value":"]","line":6,"start":107,"end":108},{"type":"T_ASSIGN","context":"normal","value":"=","line":6,"start":109,"end":110},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":6,"start":111,"end":114},{"type":"T_LBRACKET","context":"normal","value":"[","line":6,"start":114,"end":115},{"type":"T_IDENTIFIER","context":"normal","value":"key","line":6,"start":115,"end":118},{"type":"T_RBRACKET","context":"normal","value":"]","line":6,"start":118,"end":119},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":119,"end":120},{"type":"T_RCURLY","context":"normal","value":"}","line":7,"start":123,"end":124},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":124,"end":125},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":125,"end":126},{"type":"T_RETURN","context":"normal","value":"return","line":9,"start":130,"end":136},{"type":"T_LPAREN","context":"normal","value":"(","line":9,"start":137,"end":138},{"type":"T_LPAREN","context":"normal","value":"(","line":9,"start":138,"end":139},{"type":"T_IDENTIFIER","context":"normal","value":"clone","line":9,"start":139,"end":144},{"type":"T_COLON","context":"type","value":":","line":9,"start":144,"end":145},{"type":"T_ANY_TYPE","context":"type","value":"any","line":9,"start":146,"end":149},{"type":"T_RPAREN","context":"normal","value":")","line":9,"start":149,"end":150},{"type":"T_COLON","context":"type","value":":","line":9,"start":150,"end":151},{"type":"T_TYPEOF","context":"type","value":"typeof","line":9,"start":152,"end":158},{"type":"T_IDENTIFIER","context":"type","value":"obj","line":9,"start":159,"end":162},{"type":"T_RPAREN","context":"normal","value":")","line":9,"start":162,"end":163},{"type":"T_SEMICOLON","context":"normal","value":";","line":9,"start":163,"end":164},{"type":"Line","context":"comment","value":"// <<","line":9,"start":165,"end":170},{"type":"T_RCURLY","context":"normal","value":"}","line":10,"start":171,"end":172},{"type":"T_CONST","context":"normal","value":"const","line":12,"start":174,"end":179},{"type":"T_IDENTIFIER","context":"normal","value":"clone","line":12,"start":180,"end":185},{"type":"T_ASSIGN","context":"normal","value":"=","line":12,"start":186,"end":187},{"type":"T_IDENTIFIER","context":"normal","value":"cloneObject","line":12,"start":188,"end":199},{"type":"T_LPAREN","context":"normal","value":"(","line":12,"start":199,"end":200},{"type":"T_LCURLY","context":"normal","value":"{","line":12,"start":200,"end":201},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":13,"start":204,"end":207},{"type":"T_COLON","context":"normal","value":":","line":13,"start":207,"end":208},{"type":"T_NUMBER","context":"normal","value":"1","line":13,"start":209,"end":210},{"type":"T_COMMA","context":"normal","value":",","line":13,"start":210,"end":211},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":14,"start":214,"end":217},{"type":"T_COLON","context":"normal","value":":","line":14,"start":217,"end":218},{"type":"T_TRUE","context":"normal","value":"true","line":14,"start":219,"end":223},{"type":"T_COMMA","context":"normal","value":",","line":14,"start":223,"end":224},{"type":"T_IDENTIFIER","context":"normal","value":"baz","line":15,"start":227,"end":230},{"type":"T_COLON","context":"normal","value":":","line":15,"start":230,"end":231},{"type":"T_STRING","context":"normal","value":"'three'","line":15,"start":232,"end":239},{"type":"T_RCURLY","context":"normal","value":"}","line":16,"start":240,"end":241},{"type":"T_RPAREN","context":"normal","value":")","line":16,"start":241,"end":242},{"type":"T_SEMICOLON","context":"normal","value":";","line":16,"start":242,"end":243},{"type":"T_LPAREN","context":"normal","value":"(","line":18,"start":245,"end":246},{"type":"T_IDENTIFIER","context":"normal","value":"clone","line":18,"start":246,"end":251},{"type":"T_PERIOD","context":"normal","value":".","line":18,"start":251,"end":252},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":18,"start":252,"end":255},{"type":"T_COLON","context":"type","value":":","line":18,"start":255,"end":256},{"type":"T_NUMBER_SINGLETON_TYPE","context":"type","value":"1","line":18,"start":257,"end":258},{"type":"T_RPAREN","context":"normal","value":")","line":18,"start":258,"end":259},{"type":"T_SEMICOLON","context":"normal","value":";","line":18,"start":259,"end":260},{"type":"Line","context":"comment","value":"// Works!","line":18,"start":267,"end":276},{"type":"T_LPAREN","context":"normal","value":"(","line":19,"start":277,"end":278},{"type":"T_IDENTIFIER","context":"normal","value":"clone","line":19,"start":278,"end":283},{"type":"T_PERIOD","context":"normal","value":".","line":19,"start":283,"end":284},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":19,"start":284,"end":287},{"type":"T_COLON","context":"type","value":":","line":19,"start":287,"end":288},{"type":"T_TRUE","context":"type","value":"true","line":19,"start":289,"end":293},{"type":"T_RPAREN","context":"normal","value":")","line":19,"start":293,"end":294},{"type":"T_SEMICOLON","context":"normal","value":";","line":19,"start":294,"end":295},{"type":"Line","context":"comment","value":"// Works!","line":19,"start":299,"end":308},{"type":"T_LPAREN","context":"normal","value":"(","line":20,"start":309,"end":310},{"type":"T_IDENTIFIER","context":"normal","value":"clone","line":20,"start":310,"end":315},{"type":"T_PERIOD","context":"normal","value":".","line":20,"start":315,"end":316},{"type":"T_IDENTIFIER","context":"normal","value":"baz","line":20,"start":316,"end":319},{"type":"T_COLON","context":"type","value":":","line":20,"start":319,"end":320},{"type":"T_STRING","context":"type","value":"'three'","line":20,"start":321,"end":328},{"type":"T_RPAREN","context":"normal","value":")","line":20,"start":328,"end":329},{"type":"T_SEMICOLON","context":"normal","value":";","line":20,"start":329,"end":330},{"type":"Line","context":"comment","value":"// Works!","line":20,"start":331,"end":340}],"errors":[]}
Type checking through type assertions
If we want to validate what kinds of types are coming into our cloneObject
method from before, we could write the following annotation:
1
2
3
|
function cloneObject(obj: { [key: string]: mixed }) {
}
|
{"value":"function cloneObject(obj: { [key: string]: mixed }) {\n // ...\n}\n","tokens":[{"type":"T_FUNCTION","context":"normal","value":"function","line":1,"start":0,"end":8},{"type":"T_IDENTIFIER","context":"normal","value":"cloneObject","line":1,"start":9,"end":20},{"type":"T_LPAREN","context":"normal","value":"(","line":1,"start":20,"end":21},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":1,"start":21,"end":24},{"type":"T_COLON","context":"type","value":":","line":1,"start":24,"end":25},{"type":"T_LCURLY","context":"type","value":"{","line":1,"start":26,"end":27},{"type":"T_LBRACKET","context":"type","value":"[","line":1,"start":28,"end":29},{"type":"T_IDENTIFIER","context":"type","value":"key","line":1,"start":29,"end":32},{"type":"T_COLON","context":"type","value":":","line":1,"start":32,"end":33},{"type":"T_STRING_TYPE","context":"type","value":"string","line":1,"start":34,"end":40},{"type":"T_RBRACKET","context":"type","value":"]","line":1,"start":40,"end":41},{"type":"T_COLON","context":"type","value":":","line":1,"start":41,"end":42},{"type":"T_MIXED_TYPE","context":"type","value":"mixed","line":1,"start":43,"end":48},{"type":"T_RCURLY","context":"type","value":"}","line":1,"start":49,"end":50},{"type":"T_RPAREN","context":"normal","value":")","line":1,"start":50,"end":51},{"type":"T_LCURLY","context":"normal","value":"{","line":1,"start":52,"end":53},{"type":"Line","context":"comment","value":"// ...","line":2,"start":56,"end":62},{"type":"T_RCURLY","context":"normal","value":"}","line":3,"start":63,"end":64}],"errors":[]}
But now we have a problem. Our typeof obj
annotation also gets this new
annotation which defeats the entire purpose.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
function cloneObject(obj: { [key: string]: mixed }) {
const clone = {};
return ((clone: any): typeof obj);
}
const clone = cloneObject({
foo: 1,
bar: true,
baz: 'three'
});
(clone.foo: 1);
(clone.bar: true);
(clone.baz: 'three');
|
Cannot cast `clone.foo` to number literal `1` because mixed [1] is incompatible with number literal `1` [2]. [incompatible-cast]
Cannot cast `clone.bar` to boolean literal `true` because mixed [1] is incompatible with boolean literal `true` [2]. [incompatible-cast]
Cannot cast `clone.baz` to string literal `three` because mixed [1] is incompatible with string literal `three` [2]. [incompatible-cast]
{"value":"// @flow\nfunction cloneObject(obj: { [key: string]: mixed }) {\n const clone = {};\n // ...\n return ((clone: any): typeof obj);\n}\n\nconst clone = cloneObject({\n foo: 1,\n bar: true,\n baz: 'three'\n});\n\n// $ExpectError\n(clone.foo: 1); // Error!\n// $ExpectError\n(clone.bar: true); // Error!\n// $ExpectError\n(clone.baz: 'three'); // 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":"cloneObject","line":2,"start":18,"end":29},{"type":"T_LPAREN","context":"normal","value":"(","line":2,"start":29,"end":30},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":2,"start":30,"end":33},{"type":"T_COLON","context":"type","value":":","line":2,"start":33,"end":34},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":35,"end":36},{"type":"T_LBRACKET","context":"type","value":"[","line":2,"start":37,"end":38},{"type":"T_IDENTIFIER","context":"type","value":"key","line":2,"start":38,"end":41},{"type":"T_COLON","context":"type","value":":","line":2,"start":41,"end":42},{"type":"T_STRING_TYPE","context":"type","value":"string","line":2,"start":43,"end":49},{"type":"T_RBRACKET","context":"type","value":"]","line":2,"start":49,"end":50},{"type":"T_COLON","context":"type","value":":","line":2,"start":50,"end":51},{"type":"T_MIXED_TYPE","context":"type","value":"mixed","line":2,"start":52,"end":57},{"type":"T_RCURLY","context":"type","value":"}","line":2,"start":58,"end":59},{"type":"T_RPAREN","context":"normal","value":")","line":2,"start":59,"end":60},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":61,"end":62},{"type":"T_CONST","context":"normal","value":"const","line":3,"start":65,"end":70},{"type":"T_IDENTIFIER","context":"normal","value":"clone","line":3,"start":71,"end":76},{"type":"T_ASSIGN","context":"normal","value":"=","line":3,"start":77,"end":78},{"type":"T_LCURLY","context":"normal","value":"{","line":3,"start":79,"end":80},{"type":"T_RCURLY","context":"normal","value":"}","line":3,"start":80,"end":81},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":81,"end":82},{"type":"Line","context":"comment","value":"// ...","line":4,"start":85,"end":91},{"type":"T_RETURN","context":"normal","value":"return","line":5,"start":94,"end":100},{"type":"T_LPAREN","context":"normal","value":"(","line":5,"start":101,"end":102},{"type":"T_LPAREN","context":"normal","value":"(","line":5,"start":102,"end":103},{"type":"T_IDENTIFIER","context":"normal","value":"clone","line":5,"start":103,"end":108},{"type":"T_COLON","context":"type","value":":","line":5,"start":108,"end":109},{"type":"T_ANY_TYPE","context":"type","value":"any","line":5,"start":110,"end":113},{"type":"T_RPAREN","context":"normal","value":")","line":5,"start":113,"end":114},{"type":"T_COLON","context":"type","value":":","line":5,"start":114,"end":115},{"type":"T_TYPEOF","context":"type","value":"typeof","line":5,"start":116,"end":122},{"type":"T_IDENTIFIER","context":"type","value":"obj","line":5,"start":123,"end":126},{"type":"T_RPAREN","context":"normal","value":")","line":5,"start":126,"end":127},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":127,"end":128},{"type":"T_RCURLY","context":"normal","value":"}","line":6,"start":129,"end":130},{"type":"T_CONST","context":"normal","value":"const","line":8,"start":132,"end":137},{"type":"T_IDENTIFIER","context":"normal","value":"clone","line":8,"start":138,"end":143},{"type":"T_ASSIGN","context":"normal","value":"=","line":8,"start":144,"end":145},{"type":"T_IDENTIFIER","context":"normal","value":"cloneObject","line":8,"start":146,"end":157},{"type":"T_LPAREN","context":"normal","value":"(","line":8,"start":157,"end":158},{"type":"T_LCURLY","context":"normal","value":"{","line":8,"start":158,"end":159},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":9,"start":162,"end":165},{"type":"T_COLON","context":"normal","value":":","line":9,"start":165,"end":166},{"type":"T_NUMBER","context":"normal","value":"1","line":9,"start":167,"end":168},{"type":"T_COMMA","context":"normal","value":",","line":9,"start":168,"end":169},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":10,"start":172,"end":175},{"type":"T_COLON","context":"normal","value":":","line":10,"start":175,"end":176},{"type":"T_TRUE","context":"normal","value":"true","line":10,"start":177,"end":181},{"type":"T_COMMA","context":"normal","value":",","line":10,"start":181,"end":182},{"type":"T_IDENTIFIER","context":"normal","value":"baz","line":11,"start":185,"end":188},{"type":"T_COLON","context":"normal","value":":","line":11,"start":188,"end":189},{"type":"T_STRING","context":"normal","value":"'three'","line":11,"start":190,"end":197},{"type":"T_RCURLY","context":"normal","value":"}","line":12,"start":198,"end":199},{"type":"T_RPAREN","context":"normal","value":")","line":12,"start":199,"end":200},{"type":"T_SEMICOLON","context":"normal","value":";","line":12,"start":200,"end":201},{"type":"Line","context":"comment","value":"// $ExpectError","line":14,"start":203,"end":218},{"type":"T_LPAREN","context":"normal","value":"(","line":15,"start":219,"end":220},{"type":"T_IDENTIFIER","context":"normal","value":"clone","line":15,"start":220,"end":225},{"type":"T_PERIOD","context":"normal","value":".","line":15,"start":225,"end":226},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":15,"start":226,"end":229},{"type":"T_COLON","context":"type","value":":","line":15,"start":229,"end":230},{"type":"T_NUMBER_SINGLETON_TYPE","context":"type","value":"1","line":15,"start":231,"end":232},{"type":"T_RPAREN","context":"normal","value":")","line":15,"start":232,"end":233},{"type":"T_SEMICOLON","context":"normal","value":";","line":15,"start":233,"end":234},{"type":"Line","context":"comment","value":"// Error!","line":15,"start":241,"end":250},{"type":"Line","context":"comment","value":"// $ExpectError","line":16,"start":251,"end":266},{"type":"T_LPAREN","context":"normal","value":"(","line":17,"start":267,"end":268},{"type":"T_IDENTIFIER","context":"normal","value":"clone","line":17,"start":268,"end":273},{"type":"T_PERIOD","context":"normal","value":".","line":17,"start":273,"end":274},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":17,"start":274,"end":277},{"type":"T_COLON","context":"type","value":":","line":17,"start":277,"end":278},{"type":"T_TRUE","context":"type","value":"true","line":17,"start":279,"end":283},{"type":"T_RPAREN","context":"normal","value":")","line":17,"start":283,"end":284},{"type":"T_SEMICOLON","context":"normal","value":";","line":17,"start":284,"end":285},{"type":"Line","context":"comment","value":"// Error!","line":17,"start":289,"end":298},{"type":"Line","context":"comment","value":"// $ExpectError","line":18,"start":299,"end":314},{"type":"T_LPAREN","context":"normal","value":"(","line":19,"start":315,"end":316},{"type":"T_IDENTIFIER","context":"normal","value":"clone","line":19,"start":316,"end":321},{"type":"T_PERIOD","context":"normal","value":".","line":19,"start":321,"end":322},{"type":"T_IDENTIFIER","context":"normal","value":"baz","line":19,"start":322,"end":325},{"type":"T_COLON","context":"type","value":":","line":19,"start":325,"end":326},{"type":"T_STRING","context":"type","value":"'three'","line":19,"start":327,"end":334},{"type":"T_RPAREN","context":"normal","value":")","line":19,"start":334,"end":335},{"type":"T_SEMICOLON","context":"normal","value":";","line":19,"start":335,"end":336},{"type":"Line","context":"comment","value":"// Error!","line":19,"start":337,"end":346}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot cast `clone.foo` to number literal `1` because mixed [1] is incompatible with number literal `1` [2]. [incompatible-cast]","context":"(clone.foo: 1); // Error!","source":"-","start":{"line":15,"column":2,"offset":220},"end":{"line":15,"column":10,"offset":229}}],"operation":null},{"id":"E2","messages":[{"id":"E2M1","description":"Cannot cast `clone.bar` to boolean literal `true` because mixed [1] is incompatible with boolean literal `true` [2]. [incompatible-cast]","context":"(clone.bar: true); // Error!","source":"-","start":{"line":17,"column":2,"offset":268},"end":{"line":17,"column":10,"offset":277}}],"operation":null},{"id":"E3","messages":[{"id":"E3M1","description":"Cannot cast `clone.baz` to string literal `three` because mixed [1] is incompatible with string literal `three` [2]. [incompatible-cast]","context":"(clone.baz: 'three'); // Error!","source":"-","start":{"line":19,"column":2,"offset":316},"end":{"line":19,"column":10,"offset":325}}],"operation":null}]}
Instead we can assert the type within the function using a type assertion and
now we’re validating our inputs.
1
2
3
4
5
6
7
8
9
|
function cloneObject(obj) {
(obj: { [key: string]: mixed });
}
cloneObject({ foo: 1 });
cloneObject([1, 2, 3]);
|
Cannot cast `obj` to object type because array literal [1] is incompatible with object type [2]. [incompatible-cast]
{"value":"// @flow\nfunction cloneObject(obj) {\n (obj: { [key: string]: mixed });\n // ...\n}\n\ncloneObject({ foo: 1 }); // Works!\n// $ExpectError\ncloneObject([1, 2, 3]); // 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":"cloneObject","line":2,"start":18,"end":29},{"type":"T_LPAREN","context":"normal","value":"(","line":2,"start":29,"end":30},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":2,"start":30,"end":33},{"type":"T_RPAREN","context":"normal","value":")","line":2,"start":33,"end":34},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":35,"end":36},{"type":"T_LPAREN","context":"normal","value":"(","line":3,"start":39,"end":40},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":3,"start":40,"end":43},{"type":"T_COLON","context":"type","value":":","line":3,"start":43,"end":44},{"type":"T_LCURLY","context":"type","value":"{","line":3,"start":45,"end":46},{"type":"T_LBRACKET","context":"type","value":"[","line":3,"start":47,"end":48},{"type":"T_IDENTIFIER","context":"type","value":"key","line":3,"start":48,"end":51},{"type":"T_COLON","context":"type","value":":","line":3,"start":51,"end":52},{"type":"T_STRING_TYPE","context":"type","value":"string","line":3,"start":53,"end":59},{"type":"T_RBRACKET","context":"type","value":"]","line":3,"start":59,"end":60},{"type":"T_COLON","context":"type","value":":","line":3,"start":60,"end":61},{"type":"T_MIXED_TYPE","context":"type","value":"mixed","line":3,"start":62,"end":67},{"type":"T_RCURLY","context":"type","value":"}","line":3,"start":68,"end":69},{"type":"T_RPAREN","context":"normal","value":")","line":3,"start":69,"end":70},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":70,"end":71},{"type":"Line","context":"comment","value":"// ...","line":4,"start":74,"end":80},{"type":"T_RCURLY","context":"normal","value":"}","line":5,"start":81,"end":82},{"type":"T_IDENTIFIER","context":"normal","value":"cloneObject","line":7,"start":84,"end":95},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":95,"end":96},{"type":"T_LCURLY","context":"normal","value":"{","line":7,"start":96,"end":97},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":7,"start":98,"end":101},{"type":"T_COLON","context":"normal","value":":","line":7,"start":101,"end":102},{"type":"T_NUMBER","context":"normal","value":"1","line":7,"start":103,"end":104},{"type":"T_RCURLY","context":"normal","value":"}","line":7,"start":105,"end":106},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":106,"end":107},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":107,"end":108},{"type":"Line","context":"comment","value":"// Works!","line":7,"start":109,"end":118},{"type":"Line","context":"comment","value":"// $ExpectError","line":8,"start":119,"end":134},{"type":"T_IDENTIFIER","context":"normal","value":"cloneObject","line":9,"start":135,"end":146},{"type":"T_LPAREN","context":"normal","value":"(","line":9,"start":146,"end":147},{"type":"T_LBRACKET","context":"normal","value":"[","line":9,"start":147,"end":148},{"type":"T_NUMBER","context":"normal","value":"1","line":9,"start":148,"end":149},{"type":"T_COMMA","context":"normal","value":",","line":9,"start":149,"end":150},{"type":"T_NUMBER","context":"normal","value":"2","line":9,"start":151,"end":152},{"type":"T_COMMA","context":"normal","value":",","line":9,"start":152,"end":153},{"type":"T_NUMBER","context":"normal","value":"3","line":9,"start":154,"end":155},{"type":"T_RBRACKET","context":"normal","value":"]","line":9,"start":155,"end":156},{"type":"T_RPAREN","context":"normal","value":")","line":9,"start":156,"end":157},{"type":"T_SEMICOLON","context":"normal","value":";","line":9,"start":157,"end":158},{"type":"Line","context":"comment","value":"// Error!","line":9,"start":160,"end":169}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot cast `obj` to object type because array literal [1] is incompatible with object type [2]. [incompatible-cast]","context":" (obj: { [key: string]: mixed });","source":"-","start":{"line":3,"column":4,"offset":40},"end":{"line":3,"column":6,"offset":43}}],"operation":null}]}
Now type inference can keep working for typeof obj
which returns the expected
shape of the object.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
function cloneObject(obj) {
(obj: { [key: string]: mixed });
const clone = {};
return ((clone: any): typeof obj);
}
const clone = cloneObject({
foo: 1,
bar: true,
baz: 'three'
});
(clone.foo: 1);
(clone.bar: true);
(clone.baz: 'three');
|
{"value":"// @flow\nfunction cloneObject(obj) {\n (obj: { [key: string]: mixed }); // <<\n\n const clone = {};\n // ...\n return ((clone: any): typeof obj);\n}\n\nconst clone = cloneObject({\n foo: 1,\n bar: true,\n baz: 'three'\n});\n\n(clone.foo: 1); // Works!\n(clone.bar: true); // Works!\n(clone.baz: 'three'); // Works!\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_FUNCTION","context":"normal","value":"function","line":2,"start":9,"end":17},{"type":"T_IDENTIFIER","context":"normal","value":"cloneObject","line":2,"start":18,"end":29},{"type":"T_LPAREN","context":"normal","value":"(","line":2,"start":29,"end":30},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":2,"start":30,"end":33},{"type":"T_RPAREN","context":"normal","value":")","line":2,"start":33,"end":34},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":35,"end":36},{"type":"T_LPAREN","context":"normal","value":"(","line":3,"start":39,"end":40},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":3,"start":40,"end":43},{"type":"T_COLON","context":"type","value":":","line":3,"start":43,"end":44},{"type":"T_LCURLY","context":"type","value":"{","line":3,"start":45,"end":46},{"type":"T_LBRACKET","context":"type","value":"[","line":3,"start":47,"end":48},{"type":"T_IDENTIFIER","context":"type","value":"key","line":3,"start":48,"end":51},{"type":"T_COLON","context":"type","value":":","line":3,"start":51,"end":52},{"type":"T_STRING_TYPE","context":"type","value":"string","line":3,"start":53,"end":59},{"type":"T_RBRACKET","context":"type","value":"]","line":3,"start":59,"end":60},{"type":"T_COLON","context":"type","value":":","line":3,"start":60,"end":61},{"type":"T_MIXED_TYPE","context":"type","value":"mixed","line":3,"start":62,"end":67},{"type":"T_RCURLY","context":"type","value":"}","line":3,"start":68,"end":69},{"type":"T_RPAREN","context":"normal","value":")","line":3,"start":69,"end":70},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":70,"end":71},{"type":"Line","context":"comment","value":"// <<","line":3,"start":72,"end":77},{"type":"T_CONST","context":"normal","value":"const","line":5,"start":81,"end":86},{"type":"T_IDENTIFIER","context":"normal","value":"clone","line":5,"start":87,"end":92},{"type":"T_ASSIGN","context":"normal","value":"=","line":5,"start":93,"end":94},{"type":"T_LCURLY","context":"normal","value":"{","line":5,"start":95,"end":96},{"type":"T_RCURLY","context":"normal","value":"}","line":5,"start":96,"end":97},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":97,"end":98},{"type":"Line","context":"comment","value":"// ...","line":6,"start":101,"end":107},{"type":"T_RETURN","context":"normal","value":"return","line":7,"start":110,"end":116},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":117,"end":118},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":118,"end":119},{"type":"T_IDENTIFIER","context":"normal","value":"clone","line":7,"start":119,"end":124},{"type":"T_COLON","context":"type","value":":","line":7,"start":124,"end":125},{"type":"T_ANY_TYPE","context":"type","value":"any","line":7,"start":126,"end":129},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":129,"end":130},{"type":"T_COLON","context":"type","value":":","line":7,"start":130,"end":131},{"type":"T_TYPEOF","context":"type","value":"typeof","line":7,"start":132,"end":138},{"type":"T_IDENTIFIER","context":"type","value":"obj","line":7,"start":139,"end":142},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":142,"end":143},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":143,"end":144},{"type":"T_RCURLY","context":"normal","value":"}","line":8,"start":145,"end":146},{"type":"T_CONST","context":"normal","value":"const","line":10,"start":148,"end":153},{"type":"T_IDENTIFIER","context":"normal","value":"clone","line":10,"start":154,"end":159},{"type":"T_ASSIGN","context":"normal","value":"=","line":10,"start":160,"end":161},{"type":"T_IDENTIFIER","context":"normal","value":"cloneObject","line":10,"start":162,"end":173},{"type":"T_LPAREN","context":"normal","value":"(","line":10,"start":173,"end":174},{"type":"T_LCURLY","context":"normal","value":"{","line":10,"start":174,"end":175},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":11,"start":178,"end":181},{"type":"T_COLON","context":"normal","value":":","line":11,"start":181,"end":182},{"type":"T_NUMBER","context":"normal","value":"1","line":11,"start":183,"end":184},{"type":"T_COMMA","context":"normal","value":",","line":11,"start":184,"end":185},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":12,"start":188,"end":191},{"type":"T_COLON","context":"normal","value":":","line":12,"start":191,"end":192},{"type":"T_TRUE","context":"normal","value":"true","line":12,"start":193,"end":197},{"type":"T_COMMA","context":"normal","value":",","line":12,"start":197,"end":198},{"type":"T_IDENTIFIER","context":"normal","value":"baz","line":13,"start":201,"end":204},{"type":"T_COLON","context":"normal","value":":","line":13,"start":204,"end":205},{"type":"T_STRING","context":"normal","value":"'three'","line":13,"start":206,"end":213},{"type":"T_RCURLY","context":"normal","value":"}","line":14,"start":214,"end":215},{"type":"T_RPAREN","context":"normal","value":")","line":14,"start":215,"end":216},{"type":"T_SEMICOLON","context":"normal","value":";","line":14,"start":216,"end":217},{"type":"T_LPAREN","context":"normal","value":"(","line":16,"start":219,"end":220},{"type":"T_IDENTIFIER","context":"normal","value":"clone","line":16,"start":220,"end":225},{"type":"T_PERIOD","context":"normal","value":".","line":16,"start":225,"end":226},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":16,"start":226,"end":229},{"type":"T_COLON","context":"type","value":":","line":16,"start":229,"end":230},{"type":"T_NUMBER_SINGLETON_TYPE","context":"type","value":"1","line":16,"start":231,"end":232},{"type":"T_RPAREN","context":"normal","value":")","line":16,"start":232,"end":233},{"type":"T_SEMICOLON","context":"normal","value":";","line":16,"start":233,"end":234},{"type":"Line","context":"comment","value":"// Works!","line":16,"start":241,"end":250},{"type":"T_LPAREN","context":"normal","value":"(","line":17,"start":251,"end":252},{"type":"T_IDENTIFIER","context":"normal","value":"clone","line":17,"start":252,"end":257},{"type":"T_PERIOD","context":"normal","value":".","line":17,"start":257,"end":258},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":17,"start":258,"end":261},{"type":"T_COLON","context":"type","value":":","line":17,"start":261,"end":262},{"type":"T_TRUE","context":"type","value":"true","line":17,"start":263,"end":267},{"type":"T_RPAREN","context":"normal","value":")","line":17,"start":267,"end":268},{"type":"T_SEMICOLON","context":"normal","value":";","line":17,"start":268,"end":269},{"type":"Line","context":"comment","value":"// Works!","line":17,"start":273,"end":282},{"type":"T_LPAREN","context":"normal","value":"(","line":18,"start":283,"end":284},{"type":"T_IDENTIFIER","context":"normal","value":"clone","line":18,"start":284,"end":289},{"type":"T_PERIOD","context":"normal","value":".","line":18,"start":289,"end":290},{"type":"T_IDENTIFIER","context":"normal","value":"baz","line":18,"start":290,"end":293},{"type":"T_COLON","context":"type","value":":","line":18,"start":293,"end":294},{"type":"T_STRING","context":"type","value":"'three'","line":18,"start":295,"end":302},{"type":"T_RPAREN","context":"normal","value":")","line":18,"start":302,"end":303},{"type":"T_SEMICOLON","context":"normal","value":";","line":18,"start":303,"end":304},{"type":"Line","context":"comment","value":"// Works!","line":18,"start":305,"end":314}],"errors":[]}
Note: This is not the proper solution to the above problem, it was being
used for demonstration only. The correct solution is annotating the function
like this:
1
2
3
|
function cloneObject<T: { [key: string]: mixed }>(obj: T): $Shape<T> {
}
|
{"value":"function cloneObject<T: { [key: string]: mixed }>(obj: T): $Shape<T> {\n // ...\n}\n","tokens":[{"type":"T_FUNCTION","context":"normal","value":"function","line":1,"start":0,"end":8},{"type":"T_IDENTIFIER","context":"normal","value":"cloneObject","line":1,"start":9,"end":20},{"type":"T_LESS_THAN","context":"type","value":"<","line":1,"start":20,"end":21},{"type":"T_IDENTIFIER","context":"type","value":"T","line":1,"start":21,"end":22},{"type":"T_COLON","context":"type","value":":","line":1,"start":22,"end":23},{"type":"T_LCURLY","context":"type","value":"{","line":1,"start":24,"end":25},{"type":"T_LBRACKET","context":"type","value":"[","line":1,"start":26,"end":27},{"type":"T_IDENTIFIER","context":"type","value":"key","line":1,"start":27,"end":30},{"type":"T_COLON","context":"type","value":":","line":1,"start":30,"end":31},{"type":"T_STRING_TYPE","context":"type","value":"string","line":1,"start":32,"end":38},{"type":"T_RBRACKET","context":"type","value":"]","line":1,"start":38,"end":39},{"type":"T_COLON","context":"type","value":":","line":1,"start":39,"end":40},{"type":"T_MIXED_TYPE","context":"type","value":"mixed","line":1,"start":41,"end":46},{"type":"T_RCURLY","context":"type","value":"}","line":1,"start":47,"end":48},{"type":"T_GREATER_THAN","context":"type","value":">","line":1,"start":48,"end":49},{"type":"T_LPAREN","context":"normal","value":"(","line":1,"start":49,"end":50},{"type":"T_IDENTIFIER","context":"normal","value":"obj","line":1,"start":50,"end":53},{"type":"T_COLON","context":"type","value":":","line":1,"start":53,"end":54},{"type":"T_IDENTIFIER","context":"type","value":"T","line":1,"start":55,"end":56},{"type":"T_RPAREN","context":"normal","value":")","line":1,"start":56,"end":57},{"type":"T_COLON","context":"type","value":":","line":1,"start":57,"end":58},{"type":"T_IDENTIFIER","context":"type","value":"$Shape","line":1,"start":59,"end":65},{"type":"T_LESS_THAN","context":"type","value":"<","line":1,"start":65,"end":66},{"type":"T_IDENTIFIER","context":"type","value":"T","line":1,"start":66,"end":67},{"type":"T_GREATER_THAN","context":"type","value":">","line":1,"start":67,"end":68},{"type":"T_LCURLY","context":"normal","value":"{","line":1,"start":69,"end":70},{"type":"Line","context":"comment","value":"// ...","line":2,"start":72,"end":78},{"type":"T_RCURLY","context":"normal","value":"}","line":3,"start":79,"end":80}],"errors":[]}