Flow provides a set of utility types to operate on other types, and can be useful for different scenarios.
Table of contents:
$Keys<T>
In Flow you can use union types similar to enums:
1
2
3
4
5
|
type Suit = "Diamonds" | "Clubs" | "Hearts" | "Spades";
const clubs: Suit = 'Clubs';
const wrong: Suit = 'wrong';
|
Cannot assign `'wrong'` to `wrong` because string [1] is incompatible with literal union [2]. [incompatible-type]
{"value":"// @flow\ntype Suit = \"Diamonds\" | \"Clubs\" | \"Hearts\" | \"Spades\";\n\nconst clubs: Suit = 'Clubs';\nconst wrong: Suit = 'wrong'; // 'wrong' is not a Suit\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":"Suit","line":2,"start":14,"end":18},{"type":"T_ASSIGN","context":"type","value":"=","line":2,"start":19,"end":20},{"type":"T_STRING","context":"type","value":"\"Diamonds\"","line":2,"start":21,"end":31},{"type":"T_BIT_OR","context":"type","value":"|","line":2,"start":32,"end":33},{"type":"T_STRING","context":"type","value":"\"Clubs\"","line":2,"start":34,"end":41},{"type":"T_BIT_OR","context":"type","value":"|","line":2,"start":42,"end":43},{"type":"T_STRING","context":"type","value":"\"Hearts\"","line":2,"start":44,"end":52},{"type":"T_BIT_OR","context":"type","value":"|","line":2,"start":53,"end":54},{"type":"T_STRING","context":"type","value":"\"Spades\"","line":2,"start":55,"end":63},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":63,"end":64},{"type":"T_CONST","context":"normal","value":"const","line":4,"start":66,"end":71},{"type":"T_IDENTIFIER","context":"normal","value":"clubs","line":4,"start":72,"end":77},{"type":"T_COLON","context":"type","value":":","line":4,"start":77,"end":78},{"type":"T_IDENTIFIER","context":"type","value":"Suit","line":4,"start":79,"end":83},{"type":"T_ASSIGN","context":"normal","value":"=","line":4,"start":84,"end":85},{"type":"T_STRING","context":"normal","value":"'Clubs'","line":4,"start":86,"end":93},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":93,"end":94},{"type":"T_CONST","context":"normal","value":"const","line":5,"start":95,"end":100},{"type":"T_IDENTIFIER","context":"normal","value":"wrong","line":5,"start":101,"end":106},{"type":"T_COLON","context":"type","value":":","line":5,"start":106,"end":107},{"type":"T_IDENTIFIER","context":"type","value":"Suit","line":5,"start":108,"end":112},{"type":"T_ASSIGN","context":"normal","value":"=","line":5,"start":113,"end":114},{"type":"T_STRING","context":"normal","value":"'wrong'","line":5,"start":115,"end":122},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":122,"end":123},{"type":"Line","context":"comment","value":"// 'wrong' is not a Suit","line":5,"start":124,"end":148}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot assign `'wrong'` to `wrong` because string [1] is incompatible with literal union [2]. [incompatible-type]","context":"const wrong: Suit = 'wrong'; // 'wrong' is not a Suit","source":"-","start":{"line":5,"column":21,"offset":115},"end":{"line":5,"column":27,"offset":122}}],"operation":null}]}
This is very handy, but sometimes you need to access the enum definition at runtime (i.e. at a value level).
Suppose for example that you want to associate a value to each suit of the previous example.
You could do
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
type Suit = "Diamonds" | "Clubs" | "Hearts" | "Spades";
const suitNumbers = {
Diamonds: 1,
Clubs: 2,
Hearts: 3,
Spades: 4
};
function printSuitNumber(suit: Suit) {
console.log(suitNumbers[suit]);
}
printSuitNumber('Diamonds');
printSuitNumber('foo');
|
Cannot call `printSuitNumber` with `'foo'` bound to `suit` because string [1] is incompatible with literal union [2]. [incompatible-call]
{"value":"// @flow\ntype Suit = \"Diamonds\" | \"Clubs\" | \"Hearts\" | \"Spades\";\n\nconst suitNumbers = {\n Diamonds: 1,\n Clubs: 2,\n Hearts: 3,\n Spades: 4\n};\n\nfunction printSuitNumber(suit: Suit) {\n console.log(suitNumbers[suit]);\n}\n\nprintSuitNumber('Diamonds'); // 1\nprintSuitNumber('foo'); // 'foo' is not a Suit\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":"Suit","line":2,"start":14,"end":18},{"type":"T_ASSIGN","context":"type","value":"=","line":2,"start":19,"end":20},{"type":"T_STRING","context":"type","value":"\"Diamonds\"","line":2,"start":21,"end":31},{"type":"T_BIT_OR","context":"type","value":"|","line":2,"start":32,"end":33},{"type":"T_STRING","context":"type","value":"\"Clubs\"","line":2,"start":34,"end":41},{"type":"T_BIT_OR","context":"type","value":"|","line":2,"start":42,"end":43},{"type":"T_STRING","context":"type","value":"\"Hearts\"","line":2,"start":44,"end":52},{"type":"T_BIT_OR","context":"type","value":"|","line":2,"start":53,"end":54},{"type":"T_STRING","context":"type","value":"\"Spades\"","line":2,"start":55,"end":63},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":63,"end":64},{"type":"T_CONST","context":"normal","value":"const","line":4,"start":66,"end":71},{"type":"T_IDENTIFIER","context":"normal","value":"suitNumbers","line":4,"start":72,"end":83},{"type":"T_ASSIGN","context":"normal","value":"=","line":4,"start":84,"end":85},{"type":"T_LCURLY","context":"normal","value":"{","line":4,"start":86,"end":87},{"type":"T_IDENTIFIER","context":"normal","value":"Diamonds","line":5,"start":90,"end":98},{"type":"T_COLON","context":"normal","value":":","line":5,"start":98,"end":99},{"type":"T_NUMBER","context":"normal","value":"1","line":5,"start":100,"end":101},{"type":"T_COMMA","context":"normal","value":",","line":5,"start":101,"end":102},{"type":"T_IDENTIFIER","context":"normal","value":"Clubs","line":6,"start":105,"end":110},{"type":"T_COLON","context":"normal","value":":","line":6,"start":110,"end":111},{"type":"T_NUMBER","context":"normal","value":"2","line":6,"start":112,"end":113},{"type":"T_COMMA","context":"normal","value":",","line":6,"start":113,"end":114},{"type":"T_IDENTIFIER","context":"normal","value":"Hearts","line":7,"start":117,"end":123},{"type":"T_COLON","context":"normal","value":":","line":7,"start":123,"end":124},{"type":"T_NUMBER","context":"normal","value":"3","line":7,"start":125,"end":126},{"type":"T_COMMA","context":"normal","value":",","line":7,"start":126,"end":127},{"type":"T_IDENTIFIER","context":"normal","value":"Spades","line":8,"start":130,"end":136},{"type":"T_COLON","context":"normal","value":":","line":8,"start":136,"end":137},{"type":"T_NUMBER","context":"normal","value":"4","line":8,"start":138,"end":139},{"type":"T_RCURLY","context":"normal","value":"}","line":9,"start":140,"end":141},{"type":"T_SEMICOLON","context":"normal","value":";","line":9,"start":141,"end":142},{"type":"T_FUNCTION","context":"normal","value":"function","line":11,"start":144,"end":152},{"type":"T_IDENTIFIER","context":"normal","value":"printSuitNumber","line":11,"start":153,"end":168},{"type":"T_LPAREN","context":"normal","value":"(","line":11,"start":168,"end":169},{"type":"T_IDENTIFIER","context":"normal","value":"suit","line":11,"start":169,"end":173},{"type":"T_COLON","context":"type","value":":","line":11,"start":173,"end":174},{"type":"T_IDENTIFIER","context":"type","value":"Suit","line":11,"start":175,"end":179},{"type":"T_RPAREN","context":"normal","value":")","line":11,"start":179,"end":180},{"type":"T_LCURLY","context":"normal","value":"{","line":11,"start":181,"end":182},{"type":"T_IDENTIFIER","context":"normal","value":"console","line":12,"start":185,"end":192},{"type":"T_PERIOD","context":"normal","value":".","line":12,"start":192,"end":193},{"type":"T_IDENTIFIER","context":"normal","value":"log","line":12,"start":193,"end":196},{"type":"T_LPAREN","context":"normal","value":"(","line":12,"start":196,"end":197},{"type":"T_IDENTIFIER","context":"normal","value":"suitNumbers","line":12,"start":197,"end":208},{"type":"T_LBRACKET","context":"normal","value":"[","line":12,"start":208,"end":209},{"type":"T_IDENTIFIER","context":"normal","value":"suit","line":12,"start":209,"end":213},{"type":"T_RBRACKET","context":"normal","value":"]","line":12,"start":213,"end":214},{"type":"T_RPAREN","context":"normal","value":")","line":12,"start":214,"end":215},{"type":"T_SEMICOLON","context":"normal","value":";","line":12,"start":215,"end":216},{"type":"T_RCURLY","context":"normal","value":"}","line":13,"start":217,"end":218},{"type":"T_IDENTIFIER","context":"normal","value":"printSuitNumber","line":15,"start":220,"end":235},{"type":"T_LPAREN","context":"normal","value":"(","line":15,"start":235,"end":236},{"type":"T_STRING","context":"normal","value":"'Diamonds'","line":15,"start":236,"end":246},{"type":"T_RPAREN","context":"normal","value":")","line":15,"start":246,"end":247},{"type":"T_SEMICOLON","context":"normal","value":";","line":15,"start":247,"end":248},{"type":"Line","context":"comment","value":"// 1","line":15,"start":249,"end":253},{"type":"T_IDENTIFIER","context":"normal","value":"printSuitNumber","line":16,"start":254,"end":269},{"type":"T_LPAREN","context":"normal","value":"(","line":16,"start":269,"end":270},{"type":"T_STRING","context":"normal","value":"'foo'","line":16,"start":270,"end":275},{"type":"T_RPAREN","context":"normal","value":")","line":16,"start":275,"end":276},{"type":"T_SEMICOLON","context":"normal","value":";","line":16,"start":276,"end":277},{"type":"Line","context":"comment","value":"// 'foo' is not a Suit","line":16,"start":278,"end":300}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot call `printSuitNumber` with `'foo'` bound to `suit` because string [1] is incompatible with literal union [2]. [incompatible-call]","context":"printSuitNumber('foo'); // 'foo' is not a Suit","source":"-","start":{"line":16,"column":17,"offset":270},"end":{"line":16,"column":21,"offset":275}}],"operation":null}]}
but this doesn’t feel very DRY, as we had to explicitly define the suit names twice.
In situations like this one, you can leverage the $Keys<T>
operator. Let’s see another example, this time using $Keys
:
1
2
3
4
5
6
7
8
9
10
11
|
const countries = {
US: "United States",
IT: "Italy",
FR: "France"
};
type Country = $Keys<typeof countries>;
const italy: Country = 'IT';
const nope: Country = 'nope';
|
Cannot assign `'nope'` to `nope` because property `nope` is missing in object literal [1]. [prop-missing]
{"value":"// @flow\nconst countries = {\n US: \"United States\",\n IT: \"Italy\",\n FR: \"France\"\n};\n\ntype Country = $Keys<typeof countries>;\n\nconst italy: Country = 'IT';\nconst nope: Country = 'nope'; // 'nope' is not a Country\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_CONST","context":"normal","value":"const","line":2,"start":9,"end":14},{"type":"T_IDENTIFIER","context":"normal","value":"countries","line":2,"start":15,"end":24},{"type":"T_ASSIGN","context":"normal","value":"=","line":2,"start":25,"end":26},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":27,"end":28},{"type":"T_IDENTIFIER","context":"normal","value":"US","line":3,"start":31,"end":33},{"type":"T_COLON","context":"normal","value":":","line":3,"start":33,"end":34},{"type":"T_STRING","context":"normal","value":"\"United States\"","line":3,"start":35,"end":50},{"type":"T_COMMA","context":"normal","value":",","line":3,"start":50,"end":51},{"type":"T_IDENTIFIER","context":"normal","value":"IT","line":4,"start":54,"end":56},{"type":"T_COLON","context":"normal","value":":","line":4,"start":56,"end":57},{"type":"T_STRING","context":"normal","value":"\"Italy\"","line":4,"start":58,"end":65},{"type":"T_COMMA","context":"normal","value":",","line":4,"start":65,"end":66},{"type":"T_IDENTIFIER","context":"normal","value":"FR","line":5,"start":69,"end":71},{"type":"T_COLON","context":"normal","value":":","line":5,"start":71,"end":72},{"type":"T_STRING","context":"normal","value":"\"France\"","line":5,"start":73,"end":81},{"type":"T_RCURLY","context":"normal","value":"}","line":6,"start":82,"end":83},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":83,"end":84},{"type":"T_TYPE","context":"normal","value":"type","line":8,"start":86,"end":90},{"type":"T_IDENTIFIER","context":"type","value":"Country","line":8,"start":91,"end":98},{"type":"T_ASSIGN","context":"type","value":"=","line":8,"start":99,"end":100},{"type":"T_IDENTIFIER","context":"type","value":"$Keys","line":8,"start":101,"end":106},{"type":"T_LESS_THAN","context":"type","value":"<","line":8,"start":106,"end":107},{"type":"T_TYPEOF","context":"type","value":"typeof","line":8,"start":107,"end":113},{"type":"T_IDENTIFIER","context":"type","value":"countries","line":8,"start":114,"end":123},{"type":"T_GREATER_THAN","context":"type","value":">","line":8,"start":123,"end":124},{"type":"T_SEMICOLON","context":"normal","value":";","line":8,"start":124,"end":125},{"type":"T_CONST","context":"normal","value":"const","line":10,"start":127,"end":132},{"type":"T_IDENTIFIER","context":"normal","value":"italy","line":10,"start":133,"end":138},{"type":"T_COLON","context":"type","value":":","line":10,"start":138,"end":139},{"type":"T_IDENTIFIER","context":"type","value":"Country","line":10,"start":140,"end":147},{"type":"T_ASSIGN","context":"normal","value":"=","line":10,"start":148,"end":149},{"type":"T_STRING","context":"normal","value":"'IT'","line":10,"start":150,"end":154},{"type":"T_SEMICOLON","context":"normal","value":";","line":10,"start":154,"end":155},{"type":"T_CONST","context":"normal","value":"const","line":11,"start":156,"end":161},{"type":"T_IDENTIFIER","context":"normal","value":"nope","line":11,"start":162,"end":166},{"type":"T_COLON","context":"type","value":":","line":11,"start":166,"end":167},{"type":"T_IDENTIFIER","context":"type","value":"Country","line":11,"start":168,"end":175},{"type":"T_ASSIGN","context":"normal","value":"=","line":11,"start":176,"end":177},{"type":"T_STRING","context":"normal","value":"'nope'","line":11,"start":178,"end":184},{"type":"T_SEMICOLON","context":"normal","value":";","line":11,"start":184,"end":185},{"type":"Line","context":"comment","value":"// 'nope' is not a Country","line":11,"start":186,"end":212}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot assign `'nope'` to `nope` because property `nope` is missing in object literal [1]. [prop-missing]","context":"const nope: Country = 'nope'; // 'nope' is not a Country","source":"-","start":{"line":11,"column":23,"offset":178},"end":{"line":11,"column":28,"offset":184}}],"operation":null}]}
In the example above, the type of Country
is equivalent to type Country = 'US' | 'IT' | 'FR'
, but Flow was able to extract it from the keys of countries
.
$Values<T>
$Values<T>
represents the union type of all the value types (not the values, but their types!) of the enumerable properties in an Object Type T
.
For example:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
type Props = {
name: string,
age: number,
};
type PropValues = string | number;
type Prop$Values = $Values<Props>;
const name: Prop$Values = 'Jon';
const age: Prop$Values = 42;
const fn: Prop$Values = () => {};
|
Cannot assign function to `fn` because: [incompatible-type] Either function [1] is incompatible with string [2]. Or function [1] is incompatible with number [3].
{"value":"// @flow\ntype Props = {\n name: string,\n age: number,\n};\n\n// The following two types are equivalent:\ntype PropValues = string | number;\ntype Prop$Values = $Values<Props>;\n\nconst name: Prop$Values = 'Jon'; // OK\nconst age: Prop$Values = 42; // OK\nconst fn: Prop$Values = () => {}; // Error! function is not part of the union type\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":"Props","line":2,"start":14,"end":19},{"type":"T_ASSIGN","context":"type","value":"=","line":2,"start":20,"end":21},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":22,"end":23},{"type":"T_IDENTIFIER","context":"normal","value":"name","line":3,"start":26,"end":30},{"type":"T_COLON","context":"type","value":":","line":3,"start":30,"end":31},{"type":"T_STRING_TYPE","context":"type","value":"string","line":3,"start":32,"end":38},{"type":"T_COMMA","context":"type","value":",","line":3,"start":38,"end":39},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":4,"start":42,"end":45},{"type":"T_COLON","context":"type","value":":","line":4,"start":45,"end":46},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":4,"start":47,"end":53},{"type":"T_COMMA","context":"type","value":",","line":4,"start":53,"end":54},{"type":"T_RCURLY","context":"type","value":"}","line":5,"start":55,"end":56},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":56,"end":57},{"type":"Line","context":"comment","value":"// The following two types are equivalent:","line":7,"start":59,"end":101},{"type":"T_TYPE","context":"normal","value":"type","line":8,"start":102,"end":106},{"type":"T_IDENTIFIER","context":"type","value":"PropValues","line":8,"start":107,"end":117},{"type":"T_ASSIGN","context":"type","value":"=","line":8,"start":118,"end":119},{"type":"T_STRING_TYPE","context":"type","value":"string","line":8,"start":120,"end":126},{"type":"T_BIT_OR","context":"type","value":"|","line":8,"start":127,"end":128},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":8,"start":129,"end":135},{"type":"T_SEMICOLON","context":"normal","value":";","line":8,"start":135,"end":136},{"type":"T_TYPE","context":"normal","value":"type","line":9,"start":137,"end":141},{"type":"T_IDENTIFIER","context":"type","value":"Prop$Values","line":9,"start":142,"end":153},{"type":"T_ASSIGN","context":"type","value":"=","line":9,"start":154,"end":155},{"type":"T_IDENTIFIER","context":"type","value":"$Values","line":9,"start":156,"end":163},{"type":"T_LESS_THAN","context":"type","value":"<","line":9,"start":163,"end":164},{"type":"T_IDENTIFIER","context":"type","value":"Props","line":9,"start":164,"end":169},{"type":"T_GREATER_THAN","context":"type","value":">","line":9,"start":169,"end":170},{"type":"T_SEMICOLON","context":"normal","value":";","line":9,"start":170,"end":171},{"type":"T_CONST","context":"normal","value":"const","line":11,"start":173,"end":178},{"type":"T_IDENTIFIER","context":"normal","value":"name","line":11,"start":179,"end":183},{"type":"T_COLON","context":"type","value":":","line":11,"start":183,"end":184},{"type":"T_IDENTIFIER","context":"type","value":"Prop$Values","line":11,"start":185,"end":196},{"type":"T_ASSIGN","context":"normal","value":"=","line":11,"start":197,"end":198},{"type":"T_STRING","context":"normal","value":"'Jon'","line":11,"start":199,"end":204},{"type":"T_SEMICOLON","context":"normal","value":";","line":11,"start":204,"end":205},{"type":"Line","context":"comment","value":"// OK","line":11,"start":207,"end":212},{"type":"T_CONST","context":"normal","value":"const","line":12,"start":213,"end":218},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":12,"start":219,"end":222},{"type":"T_COLON","context":"type","value":":","line":12,"start":222,"end":223},{"type":"T_IDENTIFIER","context":"type","value":"Prop$Values","line":12,"start":224,"end":235},{"type":"T_ASSIGN","context":"normal","value":"=","line":12,"start":236,"end":237},{"type":"T_NUMBER","context":"normal","value":"42","line":12,"start":238,"end":240},{"type":"T_SEMICOLON","context":"normal","value":";","line":12,"start":240,"end":241},{"type":"Line","context":"comment","value":"// OK","line":12,"start":243,"end":248},{"type":"T_CONST","context":"normal","value":"const","line":13,"start":249,"end":254},{"type":"T_IDENTIFIER","context":"normal","value":"fn","line":13,"start":255,"end":257},{"type":"T_COLON","context":"type","value":":","line":13,"start":257,"end":258},{"type":"T_IDENTIFIER","context":"type","value":"Prop$Values","line":13,"start":259,"end":270},{"type":"T_ASSIGN","context":"normal","value":"=","line":13,"start":271,"end":272},{"type":"T_LPAREN","context":"normal","value":"(","line":13,"start":273,"end":274},{"type":"T_RPAREN","context":"normal","value":")","line":13,"start":274,"end":275},{"type":"T_ARROW","context":"normal","value":"=>","line":13,"start":276,"end":278},{"type":"T_LCURLY","context":"normal","value":"{","line":13,"start":279,"end":280},{"type":"T_RCURLY","context":"normal","value":"}","line":13,"start":280,"end":281},{"type":"T_SEMICOLON","context":"normal","value":";","line":13,"start":281,"end":282},{"type":"Line","context":"comment","value":"// Error! function is not part of the union type","line":13,"start":284,"end":332}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot assign function to `fn` because: [incompatible-type] Either function [1] is incompatible with string [2]. Or function [1] is incompatible with number [3].","context":"const fn: Prop$Values = () => {}; // Error! function is not part of the union type","source":"-","start":{"line":13,"column":25,"offset":273},"end":{"line":13,"column":32,"offset":281}}],"operation":null}]}
$ReadOnly<T>
$ReadOnly<T>
is a type that represents the read-only version of a given object type T
. A read-only object type is an object type whose keys are all read-only.
This means that the following 2 types are equivalent:
1
2
3
|
type ReadOnlyObj = {
+key: any,
};
|
{"value":"type ReadOnlyObj = {\n +key: any, // read-only field, marked by the `+` annotation\n};\n","tokens":[{"type":"T_TYPE","context":"normal","value":"type","line":1,"start":0,"end":4},{"type":"T_IDENTIFIER","context":"type","value":"ReadOnlyObj","line":1,"start":5,"end":16},{"type":"T_ASSIGN","context":"type","value":"=","line":1,"start":17,"end":18},{"type":"T_LCURLY","context":"type","value":"{","line":1,"start":19,"end":20},{"type":"T_PLUS","context":"type","value":"+","line":2,"start":23,"end":24},{"type":"T_IDENTIFIER","context":"normal","value":"key","line":2,"start":24,"end":27},{"type":"T_COLON","context":"type","value":":","line":2,"start":27,"end":28},{"type":"T_ANY_TYPE","context":"type","value":"any","line":2,"start":29,"end":32},{"type":"T_COMMA","context":"type","value":",","line":2,"start":32,"end":33},{"type":"Line","context":"comment","value":"// read-only field, marked by the `+` annotation","line":2,"start":35,"end":83},{"type":"T_RCURLY","context":"type","value":"}","line":3,"start":84,"end":85},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":85,"end":86}],"errors":[]}
1
2
3
|
type ReadOnlyObj = $ReadOnly<{
key: any,
}>;
|
{"value":"type ReadOnlyObj = $ReadOnly<{\n key: any,\n}>;\n","tokens":[{"type":"T_TYPE","context":"normal","value":"type","line":1,"start":0,"end":4},{"type":"T_IDENTIFIER","context":"type","value":"ReadOnlyObj","line":1,"start":5,"end":16},{"type":"T_ASSIGN","context":"type","value":"=","line":1,"start":17,"end":18},{"type":"T_IDENTIFIER","context":"type","value":"$ReadOnly","line":1,"start":19,"end":28},{"type":"T_LESS_THAN","context":"type","value":"<","line":1,"start":28,"end":29},{"type":"T_LCURLY","context":"type","value":"{","line":1,"start":29,"end":30},{"type":"T_IDENTIFIER","context":"normal","value":"key","line":2,"start":33,"end":36},{"type":"T_COLON","context":"type","value":":","line":2,"start":36,"end":37},{"type":"T_ANY_TYPE","context":"type","value":"any","line":2,"start":38,"end":41},{"type":"T_COMMA","context":"type","value":",","line":2,"start":41,"end":42},{"type":"T_RCURLY","context":"type","value":"}","line":3,"start":43,"end":44},{"type":"T_GREATER_THAN","context":"type","value":">","line":3,"start":44,"end":45},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":45,"end":46}],"errors":[]}
This is useful when you need to use a read-only version of an object type you’ve already defined, without manually having to re-define and annotate each key as read-only. For example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
type Props = {
name: string,
age: number,
};
type ReadOnlyProps = $ReadOnly<Props>;
function render(props: ReadOnlyProps) {
const {name, age} = props;
props.age = 42;
}
|
Cannot assign `42` to `props.age` because property `age` is not writable. [cannot-write]
{"value":"// @flow\ntype Props = {\n name: string,\n age: number,\n // ...\n};\n\ntype ReadOnlyProps = $ReadOnly<Props>;\n\nfunction render(props: ReadOnlyProps) {\n const {name, age} = props; // OK to read\n props.age = 42; // Error when writing\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":"Props","line":2,"start":14,"end":19},{"type":"T_ASSIGN","context":"type","value":"=","line":2,"start":20,"end":21},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":22,"end":23},{"type":"T_IDENTIFIER","context":"normal","value":"name","line":3,"start":26,"end":30},{"type":"T_COLON","context":"type","value":":","line":3,"start":30,"end":31},{"type":"T_STRING_TYPE","context":"type","value":"string","line":3,"start":32,"end":38},{"type":"T_COMMA","context":"type","value":",","line":3,"start":38,"end":39},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":4,"start":42,"end":45},{"type":"T_COLON","context":"type","value":":","line":4,"start":45,"end":46},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":4,"start":47,"end":53},{"type":"T_COMMA","context":"type","value":",","line":4,"start":53,"end":54},{"type":"Line","context":"comment","value":"// ...","line":5,"start":57,"end":63},{"type":"T_RCURLY","context":"type","value":"}","line":6,"start":64,"end":65},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":65,"end":66},{"type":"T_TYPE","context":"normal","value":"type","line":8,"start":68,"end":72},{"type":"T_IDENTIFIER","context":"type","value":"ReadOnlyProps","line":8,"start":73,"end":86},{"type":"T_ASSIGN","context":"type","value":"=","line":8,"start":87,"end":88},{"type":"T_IDENTIFIER","context":"type","value":"$ReadOnly","line":8,"start":89,"end":98},{"type":"T_LESS_THAN","context":"type","value":"<","line":8,"start":98,"end":99},{"type":"T_IDENTIFIER","context":"type","value":"Props","line":8,"start":99,"end":104},{"type":"T_GREATER_THAN","context":"type","value":">","line":8,"start":104,"end":105},{"type":"T_SEMICOLON","context":"normal","value":";","line":8,"start":105,"end":106},{"type":"T_FUNCTION","context":"normal","value":"function","line":10,"start":108,"end":116},{"type":"T_IDENTIFIER","context":"normal","value":"render","line":10,"start":117,"end":123},{"type":"T_LPAREN","context":"normal","value":"(","line":10,"start":123,"end":124},{"type":"T_IDENTIFIER","context":"normal","value":"props","line":10,"start":124,"end":129},{"type":"T_COLON","context":"type","value":":","line":10,"start":129,"end":130},{"type":"T_IDENTIFIER","context":"type","value":"ReadOnlyProps","line":10,"start":131,"end":144},{"type":"T_RPAREN","context":"normal","value":")","line":10,"start":144,"end":145},{"type":"T_LCURLY","context":"normal","value":"{","line":10,"start":146,"end":147},{"type":"T_CONST","context":"normal","value":"const","line":11,"start":150,"end":155},{"type":"T_LCURLY","context":"normal","value":"{","line":11,"start":156,"end":157},{"type":"T_IDENTIFIER","context":"normal","value":"name","line":11,"start":157,"end":161},{"type":"T_COMMA","context":"normal","value":",","line":11,"start":161,"end":162},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":11,"start":163,"end":166},{"type":"T_RCURLY","context":"normal","value":"}","line":11,"start":166,"end":167},{"type":"T_ASSIGN","context":"normal","value":"=","line":11,"start":168,"end":169},{"type":"T_IDENTIFIER","context":"normal","value":"props","line":11,"start":170,"end":175},{"type":"T_SEMICOLON","context":"normal","value":";","line":11,"start":175,"end":176},{"type":"Line","context":"comment","value":"// OK to read","line":11,"start":178,"end":191},{"type":"T_IDENTIFIER","context":"normal","value":"props","line":12,"start":194,"end":199},{"type":"T_PERIOD","context":"normal","value":".","line":12,"start":199,"end":200},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":12,"start":200,"end":203},{"type":"T_ASSIGN","context":"normal","value":"=","line":12,"start":204,"end":205},{"type":"T_NUMBER","context":"normal","value":"42","line":12,"start":206,"end":208},{"type":"T_SEMICOLON","context":"normal","value":";","line":12,"start":208,"end":209},{"type":"Line","context":"comment","value":"// Error when writing","line":12,"start":222,"end":243},{"type":"Line","context":"comment","value":"// ...","line":13,"start":246,"end":252},{"type":"T_RCURLY","context":"normal","value":"}","line":14,"start":253,"end":254}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot assign `42` to `props.age` because property `age` is not writable. [cannot-write]","context":" props.age = 42; // Error when writing","source":"-","start":{"line":12,"column":9,"offset":200},"end":{"line":12,"column":11,"offset":203}}],"operation":null}]}
Additionally, other utility types, such as $ObjMap<T>
, may strip any read/write annotations, so $ReadOnly<T>
is a handy way to quickly make the object read-only again after operating on it:
1
2
3
4
5
|
type Obj = {
+key: any,
};
type MappedObj = $ReadOnly<$ObjMap<Obj, TypeFn>>
|
{"value":"type Obj = {\n +key: any,\n};\n\ntype MappedObj = $ReadOnly<$ObjMap<Obj, TypeFn>> // Still read-only\n","tokens":[{"type":"T_TYPE","context":"normal","value":"type","line":1,"start":0,"end":4},{"type":"T_IDENTIFIER","context":"type","value":"Obj","line":1,"start":5,"end":8},{"type":"T_ASSIGN","context":"type","value":"=","line":1,"start":9,"end":10},{"type":"T_LCURLY","context":"type","value":"{","line":1,"start":11,"end":12},{"type":"T_PLUS","context":"type","value":"+","line":2,"start":15,"end":16},{"type":"T_IDENTIFIER","context":"normal","value":"key","line":2,"start":16,"end":19},{"type":"T_COLON","context":"type","value":":","line":2,"start":19,"end":20},{"type":"T_ANY_TYPE","context":"type","value":"any","line":2,"start":21,"end":24},{"type":"T_COMMA","context":"type","value":",","line":2,"start":24,"end":25},{"type":"T_RCURLY","context":"type","value":"}","line":3,"start":26,"end":27},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":27,"end":28},{"type":"T_TYPE","context":"normal","value":"type","line":5,"start":30,"end":34},{"type":"T_IDENTIFIER","context":"type","value":"MappedObj","line":5,"start":35,"end":44},{"type":"T_ASSIGN","context":"type","value":"=","line":5,"start":45,"end":46},{"type":"T_IDENTIFIER","context":"type","value":"$ReadOnly","line":5,"start":47,"end":56},{"type":"T_LESS_THAN","context":"type","value":"<","line":5,"start":56,"end":57},{"type":"T_IDENTIFIER","context":"type","value":"$ObjMap","line":5,"start":57,"end":64},{"type":"T_LESS_THAN","context":"type","value":"<","line":5,"start":64,"end":65},{"type":"T_IDENTIFIER","context":"type","value":"Obj","line":5,"start":65,"end":68},{"type":"T_COMMA","context":"type","value":",","line":5,"start":68,"end":69},{"type":"T_IDENTIFIER","context":"type","value":"TypeFn","line":5,"start":70,"end":76},{"type":"T_GREATER_THAN","context":"type","value":">","line":5,"start":76,"end":77},{"type":"T_GREATER_THAN","context":"type","value":">","line":5,"start":77,"end":78},{"type":"Line","context":"comment","value":"// Still read-only","line":5,"start":79,"end":97}],"errors":[]}
Note: $ReadOnly
is only for making read-only object types. See the Array docs
for how to type read-only arrays with $ReadOnlyArray
.
$Exact<T>
$Exact<{name: string}>
is a synonym for {| name: string |}
as in the Object documentation.
1
2
3
4
5
6
7
8
|
type ExactUser = $Exact<{name: string}>;
type ExactUserShorthand = {| name: string |};
const user2 = {name: 'John Wilkes Booth'};
(user2: ExactUser);
(user2: ExactUserShorthand);
|
{"value":"// @flow\ntype ExactUser = $Exact<{name: string}>;\ntype ExactUserShorthand = {| name: string |};\n\nconst user2 = {name: 'John Wilkes Booth'};\n// These will both be satisfied because they are equivalent\n(user2: ExactUser);\n(user2: ExactUserShorthand);\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":"ExactUser","line":2,"start":14,"end":23},{"type":"T_ASSIGN","context":"type","value":"=","line":2,"start":24,"end":25},{"type":"T_IDENTIFIER","context":"type","value":"$Exact","line":2,"start":26,"end":32},{"type":"T_LESS_THAN","context":"type","value":"<","line":2,"start":32,"end":33},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":33,"end":34},{"type":"T_IDENTIFIER","context":"normal","value":"name","line":2,"start":34,"end":38},{"type":"T_COLON","context":"type","value":":","line":2,"start":38,"end":39},{"type":"T_STRING_TYPE","context":"type","value":"string","line":2,"start":40,"end":46},{"type":"T_RCURLY","context":"type","value":"}","line":2,"start":46,"end":47},{"type":"T_GREATER_THAN","context":"type","value":">","line":2,"start":47,"end":48},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":48,"end":49},{"type":"T_TYPE","context":"normal","value":"type","line":3,"start":50,"end":54},{"type":"T_IDENTIFIER","context":"type","value":"ExactUserShorthand","line":3,"start":55,"end":73},{"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":"name","line":3,"start":79,"end":83},{"type":"T_COLON","context":"type","value":":","line":3,"start":83,"end":84},{"type":"T_STRING_TYPE","context":"type","value":"string","line":3,"start":85,"end":91},{"type":"T_RCURLYBAR","context":"type","value":"|}","line":3,"start":92,"end":94},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":94,"end":95},{"type":"T_CONST","context":"normal","value":"const","line":5,"start":97,"end":102},{"type":"T_IDENTIFIER","context":"normal","value":"user2","line":5,"start":103,"end":108},{"type":"T_ASSIGN","context":"normal","value":"=","line":5,"start":109,"end":110},{"type":"T_LCURLY","context":"normal","value":"{","line":5,"start":111,"end":112},{"type":"T_IDENTIFIER","context":"normal","value":"name","line":5,"start":112,"end":116},{"type":"T_COLON","context":"normal","value":":","line":5,"start":116,"end":117},{"type":"T_STRING","context":"normal","value":"'John Wilkes Booth'","line":5,"start":118,"end":137},{"type":"T_RCURLY","context":"normal","value":"}","line":5,"start":137,"end":138},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":138,"end":139},{"type":"Line","context":"comment","value":"// These will both be satisfied because they are equivalent","line":6,"start":140,"end":199},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":200,"end":201},{"type":"T_IDENTIFIER","context":"normal","value":"user2","line":7,"start":201,"end":206},{"type":"T_COLON","context":"type","value":":","line":7,"start":206,"end":207},{"type":"T_IDENTIFIER","context":"type","value":"ExactUser","line":7,"start":208,"end":217},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":217,"end":218},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":218,"end":219},{"type":"T_LPAREN","context":"normal","value":"(","line":8,"start":220,"end":221},{"type":"T_IDENTIFIER","context":"normal","value":"user2","line":8,"start":221,"end":226},{"type":"T_COLON","context":"type","value":":","line":8,"start":226,"end":227},{"type":"T_IDENTIFIER","context":"type","value":"ExactUserShorthand","line":8,"start":228,"end":246},{"type":"T_RPAREN","context":"normal","value":")","line":8,"start":246,"end":247},{"type":"T_SEMICOLON","context":"normal","value":";","line":8,"start":247,"end":248}],"errors":[]}
$Diff<A, B>
As the name hints, $Diff<A, B>
is the type representing the set difference of A
and B
, i.e. A \ B
, where A
and B
are both object types. Here’s an example:
1
2
3
4
5
6
7
8
9
10
11
12
|
type Props = { name: string, age: number };
type DefaultProps = { age: number };
type RequiredProps = $Diff<Props, DefaultProps>;
function setProps(props: RequiredProps) {
}
setProps({ name: 'foo' });
setProps({ name: 'foo', age: 42, baz: false });
setProps({ age: 42 });
|
Cannot call `setProps` with object literal bound to `props` because property `name` is missing in object literal [1] but exists in `Props` [2]. [prop-missing]
{"value":"// @flow\ntype Props = { name: string, age: number };\ntype DefaultProps = { age: number };\ntype RequiredProps = $Diff<Props, DefaultProps>;\n\nfunction setProps(props: RequiredProps) {\n // ...\n}\n\nsetProps({ name: 'foo' });\nsetProps({ name: 'foo', age: 42, baz: false }); // you can pass extra props too\nsetProps({ age: 42 }); // error, name is required\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":"Props","line":2,"start":14,"end":19},{"type":"T_ASSIGN","context":"type","value":"=","line":2,"start":20,"end":21},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":22,"end":23},{"type":"T_IDENTIFIER","context":"normal","value":"name","line":2,"start":24,"end":28},{"type":"T_COLON","context":"type","value":":","line":2,"start":28,"end":29},{"type":"T_STRING_TYPE","context":"type","value":"string","line":2,"start":30,"end":36},{"type":"T_COMMA","context":"type","value":",","line":2,"start":36,"end":37},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":2,"start":38,"end":41},{"type":"T_COLON","context":"type","value":":","line":2,"start":41,"end":42},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":2,"start":43,"end":49},{"type":"T_RCURLY","context":"type","value":"}","line":2,"start":50,"end":51},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":51,"end":52},{"type":"T_TYPE","context":"normal","value":"type","line":3,"start":53,"end":57},{"type":"T_IDENTIFIER","context":"type","value":"DefaultProps","line":3,"start":58,"end":70},{"type":"T_ASSIGN","context":"type","value":"=","line":3,"start":71,"end":72},{"type":"T_LCURLY","context":"type","value":"{","line":3,"start":73,"end":74},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":3,"start":75,"end":78},{"type":"T_COLON","context":"type","value":":","line":3,"start":78,"end":79},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":3,"start":80,"end":86},{"type":"T_RCURLY","context":"type","value":"}","line":3,"start":87,"end":88},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":88,"end":89},{"type":"T_TYPE","context":"normal","value":"type","line":4,"start":90,"end":94},{"type":"T_IDENTIFIER","context":"type","value":"RequiredProps","line":4,"start":95,"end":108},{"type":"T_ASSIGN","context":"type","value":"=","line":4,"start":109,"end":110},{"type":"T_IDENTIFIER","context":"type","value":"$Diff","line":4,"start":111,"end":116},{"type":"T_LESS_THAN","context":"type","value":"<","line":4,"start":116,"end":117},{"type":"T_IDENTIFIER","context":"type","value":"Props","line":4,"start":117,"end":122},{"type":"T_COMMA","context":"type","value":",","line":4,"start":122,"end":123},{"type":"T_IDENTIFIER","context":"type","value":"DefaultProps","line":4,"start":124,"end":136},{"type":"T_GREATER_THAN","context":"type","value":">","line":4,"start":136,"end":137},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":137,"end":138},{"type":"T_FUNCTION","context":"normal","value":"function","line":6,"start":140,"end":148},{"type":"T_IDENTIFIER","context":"normal","value":"setProps","line":6,"start":149,"end":157},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":157,"end":158},{"type":"T_IDENTIFIER","context":"normal","value":"props","line":6,"start":158,"end":163},{"type":"T_COLON","context":"type","value":":","line":6,"start":163,"end":164},{"type":"T_IDENTIFIER","context":"type","value":"RequiredProps","line":6,"start":165,"end":178},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":178,"end":179},{"type":"T_LCURLY","context":"normal","value":"{","line":6,"start":180,"end":181},{"type":"Line","context":"comment","value":"// ...","line":7,"start":184,"end":190},{"type":"T_RCURLY","context":"normal","value":"}","line":8,"start":191,"end":192},{"type":"T_IDENTIFIER","context":"normal","value":"setProps","line":10,"start":194,"end":202},{"type":"T_LPAREN","context":"normal","value":"(","line":10,"start":202,"end":203},{"type":"T_LCURLY","context":"normal","value":"{","line":10,"start":203,"end":204},{"type":"T_IDENTIFIER","context":"normal","value":"name","line":10,"start":205,"end":209},{"type":"T_COLON","context":"normal","value":":","line":10,"start":209,"end":210},{"type":"T_STRING","context":"normal","value":"'foo'","line":10,"start":211,"end":216},{"type":"T_RCURLY","context":"normal","value":"}","line":10,"start":217,"end":218},{"type":"T_RPAREN","context":"normal","value":")","line":10,"start":218,"end":219},{"type":"T_SEMICOLON","context":"normal","value":";","line":10,"start":219,"end":220},{"type":"T_IDENTIFIER","context":"normal","value":"setProps","line":11,"start":221,"end":229},{"type":"T_LPAREN","context":"normal","value":"(","line":11,"start":229,"end":230},{"type":"T_LCURLY","context":"normal","value":"{","line":11,"start":230,"end":231},{"type":"T_IDENTIFIER","context":"normal","value":"name","line":11,"start":232,"end":236},{"type":"T_COLON","context":"normal","value":":","line":11,"start":236,"end":237},{"type":"T_STRING","context":"normal","value":"'foo'","line":11,"start":238,"end":243},{"type":"T_COMMA","context":"normal","value":",","line":11,"start":243,"end":244},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":11,"start":245,"end":248},{"type":"T_COLON","context":"normal","value":":","line":11,"start":248,"end":249},{"type":"T_NUMBER","context":"normal","value":"42","line":11,"start":250,"end":252},{"type":"T_COMMA","context":"normal","value":",","line":11,"start":252,"end":253},{"type":"T_IDENTIFIER","context":"normal","value":"baz","line":11,"start":254,"end":257},{"type":"T_COLON","context":"normal","value":":","line":11,"start":257,"end":258},{"type":"T_FALSE","context":"normal","value":"false","line":11,"start":259,"end":264},{"type":"T_RCURLY","context":"normal","value":"}","line":11,"start":265,"end":266},{"type":"T_RPAREN","context":"normal","value":")","line":11,"start":266,"end":267},{"type":"T_SEMICOLON","context":"normal","value":";","line":11,"start":267,"end":268},{"type":"Line","context":"comment","value":"// you can pass extra props too","line":11,"start":269,"end":300},{"type":"T_IDENTIFIER","context":"normal","value":"setProps","line":12,"start":301,"end":309},{"type":"T_LPAREN","context":"normal","value":"(","line":12,"start":309,"end":310},{"type":"T_LCURLY","context":"normal","value":"{","line":12,"start":310,"end":311},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":12,"start":312,"end":315},{"type":"T_COLON","context":"normal","value":":","line":12,"start":315,"end":316},{"type":"T_NUMBER","context":"normal","value":"42","line":12,"start":317,"end":319},{"type":"T_RCURLY","context":"normal","value":"}","line":12,"start":320,"end":321},{"type":"T_RPAREN","context":"normal","value":")","line":12,"start":321,"end":322},{"type":"T_SEMICOLON","context":"normal","value":";","line":12,"start":322,"end":323},{"type":"Line","context":"comment","value":"// error, name is required","line":12,"start":324,"end":350}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot call `setProps` with object literal bound to `props` because property `name` is missing in object literal [1] but exists in `Props` [2]. [prop-missing]","context":"setProps({ age: 42 }); // error, name is required","source":"-","start":{"line":12,"column":10,"offset":310},"end":{"line":12,"column":20,"offset":321}}],"operation":null}]}
As you may have noticed, the example is not a random one. $Diff
is exactly what the React definition file uses to define the type of the props accepted by a React Component.
Note that $Diff<A, B>
will error if the object you are removing properties from does not have the property being removed, i.e. if B
has a key that doesn’t exist in A
:
1
2
3
4
5
6
7
8
9
|
type Props = { name: string, age: number };
type DefaultProps = { age: number, other: string };
type RequiredProps = $Diff<Props, DefaultProps>;
function setProps(props: RequiredProps) {
props.name;
}
|
Cannot instantiate `$Diff` because undefined property `other` [1] is incompatible with string [2]. [incompatible-type]
{"value":"// @flow\ntype Props = { name: string, age: number };\ntype DefaultProps = { age: number, other: string }; // Will error due to this `other` property not being in Props.\ntype RequiredProps = $Diff<Props, DefaultProps>;\n\nfunction setProps(props: RequiredProps) {\n props.name;\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":"Props","line":2,"start":14,"end":19},{"type":"T_ASSIGN","context":"type","value":"=","line":2,"start":20,"end":21},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":22,"end":23},{"type":"T_IDENTIFIER","context":"normal","value":"name","line":2,"start":24,"end":28},{"type":"T_COLON","context":"type","value":":","line":2,"start":28,"end":29},{"type":"T_STRING_TYPE","context":"type","value":"string","line":2,"start":30,"end":36},{"type":"T_COMMA","context":"type","value":",","line":2,"start":36,"end":37},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":2,"start":38,"end":41},{"type":"T_COLON","context":"type","value":":","line":2,"start":41,"end":42},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":2,"start":43,"end":49},{"type":"T_RCURLY","context":"type","value":"}","line":2,"start":50,"end":51},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":51,"end":52},{"type":"T_TYPE","context":"normal","value":"type","line":3,"start":53,"end":57},{"type":"T_IDENTIFIER","context":"type","value":"DefaultProps","line":3,"start":58,"end":70},{"type":"T_ASSIGN","context":"type","value":"=","line":3,"start":71,"end":72},{"type":"T_LCURLY","context":"type","value":"{","line":3,"start":73,"end":74},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":3,"start":75,"end":78},{"type":"T_COLON","context":"type","value":":","line":3,"start":78,"end":79},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":3,"start":80,"end":86},{"type":"T_COMMA","context":"type","value":",","line":3,"start":86,"end":87},{"type":"T_IDENTIFIER","context":"normal","value":"other","line":3,"start":88,"end":93},{"type":"T_COLON","context":"type","value":":","line":3,"start":93,"end":94},{"type":"T_STRING_TYPE","context":"type","value":"string","line":3,"start":95,"end":101},{"type":"T_RCURLY","context":"type","value":"}","line":3,"start":102,"end":103},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":103,"end":104},{"type":"Line","context":"comment","value":"// Will error due to this `other` property not being in Props.","line":3,"start":105,"end":167},{"type":"T_TYPE","context":"normal","value":"type","line":4,"start":168,"end":172},{"type":"T_IDENTIFIER","context":"type","value":"RequiredProps","line":4,"start":173,"end":186},{"type":"T_ASSIGN","context":"type","value":"=","line":4,"start":187,"end":188},{"type":"T_IDENTIFIER","context":"type","value":"$Diff","line":4,"start":189,"end":194},{"type":"T_LESS_THAN","context":"type","value":"<","line":4,"start":194,"end":195},{"type":"T_IDENTIFIER","context":"type","value":"Props","line":4,"start":195,"end":200},{"type":"T_COMMA","context":"type","value":",","line":4,"start":200,"end":201},{"type":"T_IDENTIFIER","context":"type","value":"DefaultProps","line":4,"start":202,"end":214},{"type":"T_GREATER_THAN","context":"type","value":">","line":4,"start":214,"end":215},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":215,"end":216},{"type":"T_FUNCTION","context":"normal","value":"function","line":6,"start":218,"end":226},{"type":"T_IDENTIFIER","context":"normal","value":"setProps","line":6,"start":227,"end":235},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":235,"end":236},{"type":"T_IDENTIFIER","context":"normal","value":"props","line":6,"start":236,"end":241},{"type":"T_COLON","context":"type","value":":","line":6,"start":241,"end":242},{"type":"T_IDENTIFIER","context":"type","value":"RequiredProps","line":6,"start":243,"end":256},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":256,"end":257},{"type":"T_LCURLY","context":"normal","value":"{","line":6,"start":258,"end":259},{"type":"T_IDENTIFIER","context":"normal","value":"props","line":7,"start":262,"end":267},{"type":"T_PERIOD","context":"normal","value":".","line":7,"start":267,"end":268},{"type":"T_IDENTIFIER","context":"normal","value":"name","line":7,"start":268,"end":272},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":272,"end":273},{"type":"Line","context":"comment","value":"// ...","line":8,"start":276,"end":282},{"type":"T_RCURLY","context":"normal","value":"}","line":9,"start":283,"end":284}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot instantiate `$Diff` because undefined property `other` [1] is incompatible with string [2]. [incompatible-type]","context":"type RequiredProps = $Diff<Props, DefaultProps>;","source":"-","start":{"line":4,"column":28,"offset":195},"end":{"line":4,"column":32,"offset":200}}],"operation":null}]}
As a workaround, you can specify the property not present in A
as optional. For example:
1
2
|
type A = $Diff<{}, {nope: number}>;
type B = $Diff<{}, {nope: number | void}>;
|
{"value":"type A = $Diff<{}, {nope: number}>; // Error\ntype B = $Diff<{}, {nope: number | void}>; // OK\n","tokens":[{"type":"T_TYPE","context":"normal","value":"type","line":1,"start":0,"end":4},{"type":"T_IDENTIFIER","context":"type","value":"A","line":1,"start":5,"end":6},{"type":"T_ASSIGN","context":"type","value":"=","line":1,"start":7,"end":8},{"type":"T_IDENTIFIER","context":"type","value":"$Diff","line":1,"start":9,"end":14},{"type":"T_LESS_THAN","context":"type","value":"<","line":1,"start":14,"end":15},{"type":"T_LCURLY","context":"type","value":"{","line":1,"start":15,"end":16},{"type":"T_RCURLY","context":"type","value":"}","line":1,"start":16,"end":17},{"type":"T_COMMA","context":"type","value":",","line":1,"start":17,"end":18},{"type":"T_LCURLY","context":"type","value":"{","line":1,"start":19,"end":20},{"type":"T_IDENTIFIER","context":"normal","value":"nope","line":1,"start":20,"end":24},{"type":"T_COLON","context":"type","value":":","line":1,"start":24,"end":25},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":1,"start":26,"end":32},{"type":"T_RCURLY","context":"type","value":"}","line":1,"start":32,"end":33},{"type":"T_GREATER_THAN","context":"type","value":">","line":1,"start":33,"end":34},{"type":"T_SEMICOLON","context":"normal","value":";","line":1,"start":34,"end":35},{"type":"Line","context":"comment","value":"// Error","line":1,"start":36,"end":44},{"type":"T_TYPE","context":"normal","value":"type","line":2,"start":45,"end":49},{"type":"T_IDENTIFIER","context":"type","value":"B","line":2,"start":50,"end":51},{"type":"T_ASSIGN","context":"type","value":"=","line":2,"start":52,"end":53},{"type":"T_IDENTIFIER","context":"type","value":"$Diff","line":2,"start":54,"end":59},{"type":"T_LESS_THAN","context":"type","value":"<","line":2,"start":59,"end":60},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":60,"end":61},{"type":"T_RCURLY","context":"type","value":"}","line":2,"start":61,"end":62},{"type":"T_COMMA","context":"type","value":",","line":2,"start":62,"end":63},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":64,"end":65},{"type":"T_IDENTIFIER","context":"normal","value":"nope","line":2,"start":65,"end":69},{"type":"T_COLON","context":"type","value":":","line":2,"start":69,"end":70},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":2,"start":71,"end":77},{"type":"T_BIT_OR","context":"type","value":"|","line":2,"start":78,"end":79},{"type":"T_VOID_TYPE","context":"type","value":"void","line":2,"start":80,"end":84},{"type":"T_RCURLY","context":"type","value":"}","line":2,"start":84,"end":85},{"type":"T_GREATER_THAN","context":"type","value":">","line":2,"start":85,"end":86},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":86,"end":87},{"type":"Line","context":"comment","value":"// OK","line":2,"start":88,"end":93}],"errors":[]}
$Rest<A, B>
$Rest<A, B>
is the type that represents the runtime object rest operation, e.g.: const {foo, ...rest} = obj
, where A
and B
are both object types. The resulting type from this operation will be an object type containing A
’s own properties that are not own properties in B
. In flow, we treat all properties on exact object types as own. In in-exact objects, a property may or may not be own.
For example:
1
2
3
4
5
6
7
|
type Props = { name: string, age: number };
const props: Props = {name: 'Jon', age: 42};
const {age, ...otherProps} = props;
(otherProps: $Rest<Props, {|age: number|}>);
otherProps.age;
|
Cannot get `otherProps.age` because property `age` is missing in rest of object pattern [1]. [prop-missing]
{"value":"// @flow\ntype Props = { name: string, age: number };\n\nconst props: Props = {name: 'Jon', age: 42};\nconst {age, ...otherProps} = props;\n(otherProps: $Rest<Props, {|age: number|}>);\notherProps.age; // Error\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_TYPE","context":"normal","value":"type","line":2,"start":9,"end":13},{"type":"T_IDENTIFIER","context":"type","value":"Props","line":2,"start":14,"end":19},{"type":"T_ASSIGN","context":"type","value":"=","line":2,"start":20,"end":21},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":22,"end":23},{"type":"T_IDENTIFIER","context":"normal","value":"name","line":2,"start":24,"end":28},{"type":"T_COLON","context":"type","value":":","line":2,"start":28,"end":29},{"type":"T_STRING_TYPE","context":"type","value":"string","line":2,"start":30,"end":36},{"type":"T_COMMA","context":"type","value":",","line":2,"start":36,"end":37},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":2,"start":38,"end":41},{"type":"T_COLON","context":"type","value":":","line":2,"start":41,"end":42},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":2,"start":43,"end":49},{"type":"T_RCURLY","context":"type","value":"}","line":2,"start":50,"end":51},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":51,"end":52},{"type":"T_CONST","context":"normal","value":"const","line":4,"start":54,"end":59},{"type":"T_IDENTIFIER","context":"normal","value":"props","line":4,"start":60,"end":65},{"type":"T_COLON","context":"type","value":":","line":4,"start":65,"end":66},{"type":"T_IDENTIFIER","context":"type","value":"Props","line":4,"start":67,"end":72},{"type":"T_ASSIGN","context":"normal","value":"=","line":4,"start":73,"end":74},{"type":"T_LCURLY","context":"normal","value":"{","line":4,"start":75,"end":76},{"type":"T_IDENTIFIER","context":"normal","value":"name","line":4,"start":76,"end":80},{"type":"T_COLON","context":"normal","value":":","line":4,"start":80,"end":81},{"type":"T_STRING","context":"normal","value":"'Jon'","line":4,"start":82,"end":87},{"type":"T_COMMA","context":"normal","value":",","line":4,"start":87,"end":88},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":4,"start":89,"end":92},{"type":"T_COLON","context":"normal","value":":","line":4,"start":92,"end":93},{"type":"T_NUMBER","context":"normal","value":"42","line":4,"start":94,"end":96},{"type":"T_RCURLY","context":"normal","value":"}","line":4,"start":96,"end":97},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":97,"end":98},{"type":"T_CONST","context":"normal","value":"const","line":5,"start":99,"end":104},{"type":"T_LCURLY","context":"normal","value":"{","line":5,"start":105,"end":106},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":5,"start":106,"end":109},{"type":"T_COMMA","context":"normal","value":",","line":5,"start":109,"end":110},{"type":"T_ELLIPSIS","context":"normal","value":"...","line":5,"start":111,"end":114},{"type":"T_IDENTIFIER","context":"normal","value":"otherProps","line":5,"start":114,"end":124},{"type":"T_RCURLY","context":"normal","value":"}","line":5,"start":124,"end":125},{"type":"T_ASSIGN","context":"normal","value":"=","line":5,"start":126,"end":127},{"type":"T_IDENTIFIER","context":"normal","value":"props","line":5,"start":128,"end":133},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":133,"end":134},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":135,"end":136},{"type":"T_IDENTIFIER","context":"normal","value":"otherProps","line":6,"start":136,"end":146},{"type":"T_COLON","context":"type","value":":","line":6,"start":146,"end":147},{"type":"T_IDENTIFIER","context":"type","value":"$Rest","line":6,"start":148,"end":153},{"type":"T_LESS_THAN","context":"type","value":"<","line":6,"start":153,"end":154},{"type":"T_IDENTIFIER","context":"type","value":"Props","line":6,"start":154,"end":159},{"type":"T_COMMA","context":"type","value":",","line":6,"start":159,"end":160},{"type":"T_LCURLYBAR","context":"type","value":"{|","line":6,"start":161,"end":163},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":6,"start":163,"end":166},{"type":"T_COLON","context":"type","value":":","line":6,"start":166,"end":167},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":6,"start":168,"end":174},{"type":"T_RCURLYBAR","context":"type","value":"|}","line":6,"start":174,"end":176},{"type":"T_GREATER_THAN","context":"type","value":">","line":6,"start":176,"end":177},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":177,"end":178},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":178,"end":179},{"type":"T_IDENTIFIER","context":"normal","value":"otherProps","line":7,"start":180,"end":190},{"type":"T_PERIOD","context":"normal","value":".","line":7,"start":190,"end":191},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":7,"start":191,"end":194},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":194,"end":195},{"type":"Line","context":"comment","value":"// Error","line":7,"start":197,"end":205}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot get `otherProps.age` because property `age` is missing in rest of object pattern [1]. [prop-missing]","context":"otherProps.age; // Error","source":"-","start":{"line":7,"column":12,"offset":191},"end":{"line":7,"column":14,"offset":194}}],"operation":null}]}
The main difference with $Diff<A, B>
, is that $Rest<A, B>
aims to represent the true runtime rest operation, which implies that exact object types are treated differently in $Rest<A, B>
. For example, $Rest<{|n: number|}, {}>
will result in {|n?: number|}
because an in-exact empty object may have an n
property, while $Diff<{|n: number|}, {}>
will result in {|n: number|}
.
$PropertyType<T, k>
A $PropertyType<T, k>
is the type at a given key k
. As of Flow v0.36.0, k
must be a literal string.
1
2
3
4
5
6
7
8
9
10
|
type Person = {
name: string,
age: number,
parent: Person
};
const newName: $PropertyType<Person, 'name'> = 'Toni Braxton';
const newAge: $PropertyType<Person, 'age'> = 51;
const newParent: $PropertyType<Person, 'parent'> = 'Evelyn Braxton';
|
Cannot assign `'Evelyn Braxton'` to `newParent` because string [1] is incompatible with `Person` [2]. [incompatible-type]
{"value":"// @flow\ntype Person = {\n name: string,\n age: number,\n parent: Person\n};\n\nconst newName: $PropertyType<Person, 'name'> = 'Toni Braxton';\nconst newAge: $PropertyType<Person, 'age'> = 51;\nconst newParent: $PropertyType<Person, 'parent'> = 'Evelyn Braxton';\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":"Person","line":2,"start":14,"end":20},{"type":"T_ASSIGN","context":"type","value":"=","line":2,"start":21,"end":22},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":23,"end":24},{"type":"T_IDENTIFIER","context":"normal","value":"name","line":3,"start":27,"end":31},{"type":"T_COLON","context":"type","value":":","line":3,"start":31,"end":32},{"type":"T_STRING_TYPE","context":"type","value":"string","line":3,"start":33,"end":39},{"type":"T_COMMA","context":"type","value":",","line":3,"start":39,"end":40},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":4,"start":43,"end":46},{"type":"T_COLON","context":"type","value":":","line":4,"start":46,"end":47},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":4,"start":48,"end":54},{"type":"T_COMMA","context":"type","value":",","line":4,"start":54,"end":55},{"type":"T_IDENTIFIER","context":"normal","value":"parent","line":5,"start":58,"end":64},{"type":"T_COLON","context":"type","value":":","line":5,"start":64,"end":65},{"type":"T_IDENTIFIER","context":"type","value":"Person","line":5,"start":66,"end":72},{"type":"T_RCURLY","context":"type","value":"}","line":6,"start":73,"end":74},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":74,"end":75},{"type":"T_CONST","context":"normal","value":"const","line":8,"start":77,"end":82},{"type":"T_IDENTIFIER","context":"normal","value":"newName","line":8,"start":83,"end":90},{"type":"T_COLON","context":"type","value":":","line":8,"start":90,"end":91},{"type":"T_IDENTIFIER","context":"type","value":"$PropertyType","line":8,"start":92,"end":105},{"type":"T_LESS_THAN","context":"type","value":"<","line":8,"start":105,"end":106},{"type":"T_IDENTIFIER","context":"type","value":"Person","line":8,"start":106,"end":112},{"type":"T_COMMA","context":"type","value":",","line":8,"start":112,"end":113},{"type":"T_STRING","context":"type","value":"'name'","line":8,"start":114,"end":120},{"type":"T_GREATER_THAN","context":"type","value":">","line":8,"start":120,"end":121},{"type":"T_ASSIGN","context":"normal","value":"=","line":8,"start":122,"end":123},{"type":"T_STRING","context":"normal","value":"'Toni Braxton'","line":8,"start":124,"end":138},{"type":"T_SEMICOLON","context":"normal","value":";","line":8,"start":138,"end":139},{"type":"T_CONST","context":"normal","value":"const","line":9,"start":140,"end":145},{"type":"T_IDENTIFIER","context":"normal","value":"newAge","line":9,"start":146,"end":152},{"type":"T_COLON","context":"type","value":":","line":9,"start":152,"end":153},{"type":"T_IDENTIFIER","context":"type","value":"$PropertyType","line":9,"start":154,"end":167},{"type":"T_LESS_THAN","context":"type","value":"<","line":9,"start":167,"end":168},{"type":"T_IDENTIFIER","context":"type","value":"Person","line":9,"start":168,"end":174},{"type":"T_COMMA","context":"type","value":",","line":9,"start":174,"end":175},{"type":"T_STRING","context":"type","value":"'age'","line":9,"start":176,"end":181},{"type":"T_GREATER_THAN","context":"type","value":">","line":9,"start":181,"end":182},{"type":"T_ASSIGN","context":"normal","value":"=","line":9,"start":183,"end":184},{"type":"T_NUMBER","context":"normal","value":"51","line":9,"start":185,"end":187},{"type":"T_SEMICOLON","context":"normal","value":";","line":9,"start":187,"end":188},{"type":"T_CONST","context":"normal","value":"const","line":10,"start":189,"end":194},{"type":"T_IDENTIFIER","context":"normal","value":"newParent","line":10,"start":195,"end":204},{"type":"T_COLON","context":"type","value":":","line":10,"start":204,"end":205},{"type":"T_IDENTIFIER","context":"type","value":"$PropertyType","line":10,"start":206,"end":219},{"type":"T_LESS_THAN","context":"type","value":"<","line":10,"start":219,"end":220},{"type":"T_IDENTIFIER","context":"type","value":"Person","line":10,"start":220,"end":226},{"type":"T_COMMA","context":"type","value":",","line":10,"start":226,"end":227},{"type":"T_STRING","context":"type","value":"'parent'","line":10,"start":228,"end":236},{"type":"T_GREATER_THAN","context":"type","value":">","line":10,"start":236,"end":237},{"type":"T_ASSIGN","context":"normal","value":"=","line":10,"start":238,"end":239},{"type":"T_STRING","context":"normal","value":"'Evelyn Braxton'","line":10,"start":240,"end":256},{"type":"T_SEMICOLON","context":"normal","value":";","line":10,"start":256,"end":257}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot assign `'Evelyn Braxton'` to `newParent` because string [1] is incompatible with `Person` [2]. [incompatible-type]","context":"const newParent: $PropertyType<Person, 'parent'> = 'Evelyn Braxton';","source":"-","start":{"line":10,"column":52,"offset":240},"end":{"line":10,"column":67,"offset":256}}],"operation":null}]}
This can be especially useful for referring to the type of React props, or, even the entire props
type itself.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
import React from 'react';
type Props = {
text: string,
onMouseOver: ({x: number, y: number}) => void
}
class Tooltip extends React.Component<Props> {
props: Props;
}
const someProps: $PropertyType<Tooltip, 'props'> = {
text: 'foo',
onMouseOver: (data: {x: number, y: number}) => undefined
};
const otherProps: $PropertyType<Tooltip, 'props'> = {
text: 'foo'
};
|
Cannot assign object literal to `otherProps` because property `onMouseOver` is missing in object literal [1] but exists in `Props` [2]. [prop-missing]
{"value":"// @flow\nimport React from 'react';\n\ntype Props = {\n text: string,\n onMouseOver: ({x: number, y: number}) => void\n}\n\nclass Tooltip extends React.Component<Props> {\n props: Props;\n}\n\nconst someProps: $PropertyType<Tooltip, 'props'> = {\n text: 'foo',\n onMouseOver: (data: {x: number, y: number}) => undefined\n};\n\nconst otherProps: $PropertyType<Tooltip, 'props'> = {\n text: 'foo'\n // Missing the `onMouseOver` definition\n};\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_IMPORT","context":"normal","value":"import","line":2,"start":9,"end":15},{"type":"T_IDENTIFIER","context":"normal","value":"React","line":2,"start":16,"end":21},{"type":"T_IDENTIFIER","context":"normal","value":"from","line":2,"start":22,"end":26},{"type":"T_STRING","context":"normal","value":"'react'","line":2,"start":27,"end":34},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":34,"end":35},{"type":"T_TYPE","context":"normal","value":"type","line":4,"start":37,"end":41},{"type":"T_IDENTIFIER","context":"type","value":"Props","line":4,"start":42,"end":47},{"type":"T_ASSIGN","context":"type","value":"=","line":4,"start":48,"end":49},{"type":"T_LCURLY","context":"type","value":"{","line":4,"start":50,"end":51},{"type":"T_IDENTIFIER","context":"normal","value":"text","line":5,"start":54,"end":58},{"type":"T_COLON","context":"type","value":":","line":5,"start":58,"end":59},{"type":"T_STRING_TYPE","context":"type","value":"string","line":5,"start":60,"end":66},{"type":"T_COMMA","context":"type","value":",","line":5,"start":66,"end":67},{"type":"T_IDENTIFIER","context":"normal","value":"onMouseOver","line":6,"start":70,"end":81},{"type":"T_COLON","context":"type","value":":","line":6,"start":81,"end":82},{"type":"T_LPAREN","context":"type","value":"(","line":6,"start":83,"end":84},{"type":"T_LCURLY","context":"type","value":"{","line":6,"start":84,"end":85},{"type":"T_IDENTIFIER","context":"normal","value":"x","line":6,"start":85,"end":86},{"type":"T_COLON","context":"type","value":":","line":6,"start":86,"end":87},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":6,"start":88,"end":94},{"type":"T_COMMA","context":"type","value":",","line":6,"start":94,"end":95},{"type":"T_IDENTIFIER","context":"normal","value":"y","line":6,"start":96,"end":97},{"type":"T_COLON","context":"type","value":":","line":6,"start":97,"end":98},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":6,"start":99,"end":105},{"type":"T_RCURLY","context":"type","value":"}","line":6,"start":105,"end":106},{"type":"T_RPAREN","context":"type","value":")","line":6,"start":106,"end":107},{"type":"T_ARROW","context":"type","value":"=>","line":6,"start":108,"end":110},{"type":"T_VOID_TYPE","context":"type","value":"void","line":6,"start":111,"end":115},{"type":"T_RCURLY","context":"type","value":"}","line":7,"start":116,"end":117},{"type":"T_CLASS","context":"normal","value":"class","line":9,"start":119,"end":124},{"type":"T_IDENTIFIER","context":"normal","value":"Tooltip","line":9,"start":125,"end":132},{"type":"T_EXTENDS","context":"normal","value":"extends","line":9,"start":133,"end":140},{"type":"T_IDENTIFIER","context":"normal","value":"React","line":9,"start":141,"end":146},{"type":"T_PERIOD","context":"normal","value":".","line":9,"start":146,"end":147},{"type":"T_IDENTIFIER","context":"normal","value":"Component","line":9,"start":147,"end":156},{"type":"T_LESS_THAN","context":"type","value":"<","line":9,"start":156,"end":157},{"type":"T_IDENTIFIER","context":"type","value":"Props","line":9,"start":157,"end":162},{"type":"T_GREATER_THAN","context":"type","value":">","line":9,"start":162,"end":163},{"type":"T_LCURLY","context":"normal","value":"{","line":9,"start":164,"end":165},{"type":"T_IDENTIFIER","context":"normal","value":"props","line":10,"start":168,"end":173},{"type":"T_COLON","context":"type","value":":","line":10,"start":173,"end":174},{"type":"T_IDENTIFIER","context":"type","value":"Props","line":10,"start":175,"end":180},{"type":"T_SEMICOLON","context":"normal","value":";","line":10,"start":180,"end":181},{"type":"T_RCURLY","context":"normal","value":"}","line":11,"start":182,"end":183},{"type":"T_CONST","context":"normal","value":"const","line":13,"start":185,"end":190},{"type":"T_IDENTIFIER","context":"normal","value":"someProps","line":13,"start":191,"end":200},{"type":"T_COLON","context":"type","value":":","line":13,"start":200,"end":201},{"type":"T_IDENTIFIER","context":"type","value":"$PropertyType","line":13,"start":202,"end":215},{"type":"T_LESS_THAN","context":"type","value":"<","line":13,"start":215,"end":216},{"type":"T_IDENTIFIER","context":"type","value":"Tooltip","line":13,"start":216,"end":223},{"type":"T_COMMA","context":"type","value":",","line":13,"start":223,"end":224},{"type":"T_STRING","context":"type","value":"'props'","line":13,"start":225,"end":232},{"type":"T_GREATER_THAN","context":"type","value":">","line":13,"start":232,"end":233},{"type":"T_ASSIGN","context":"normal","value":"=","line":13,"start":234,"end":235},{"type":"T_LCURLY","context":"normal","value":"{","line":13,"start":236,"end":237},{"type":"T_IDENTIFIER","context":"normal","value":"text","line":14,"start":240,"end":244},{"type":"T_COLON","context":"normal","value":":","line":14,"start":244,"end":245},{"type":"T_STRING","context":"normal","value":"'foo'","line":14,"start":246,"end":251},{"type":"T_COMMA","context":"normal","value":",","line":14,"start":251,"end":252},{"type":"T_IDENTIFIER","context":"normal","value":"onMouseOver","line":15,"start":255,"end":266},{"type":"T_COLON","context":"normal","value":":","line":15,"start":266,"end":267},{"type":"T_LPAREN","context":"normal","value":"(","line":15,"start":268,"end":269},{"type":"T_IDENTIFIER","context":"normal","value":"data","line":15,"start":269,"end":273},{"type":"T_COLON","context":"type","value":":","line":15,"start":273,"end":274},{"type":"T_LCURLY","context":"type","value":"{","line":15,"start":275,"end":276},{"type":"T_IDENTIFIER","context":"normal","value":"x","line":15,"start":276,"end":277},{"type":"T_COLON","context":"type","value":":","line":15,"start":277,"end":278},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":15,"start":279,"end":285},{"type":"T_COMMA","context":"type","value":",","line":15,"start":285,"end":286},{"type":"T_IDENTIFIER","context":"normal","value":"y","line":15,"start":287,"end":288},{"type":"T_COLON","context":"type","value":":","line":15,"start":288,"end":289},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":15,"start":290,"end":296},{"type":"T_RCURLY","context":"type","value":"}","line":15,"start":296,"end":297},{"type":"T_RPAREN","context":"normal","value":")","line":15,"start":297,"end":298},{"type":"T_ARROW","context":"normal","value":"=>","line":15,"start":299,"end":301},{"type":"T_IDENTIFIER","context":"normal","value":"undefined","line":15,"start":302,"end":311},{"type":"T_RCURLY","context":"normal","value":"}","line":16,"start":312,"end":313},{"type":"T_SEMICOLON","context":"normal","value":";","line":16,"start":313,"end":314},{"type":"T_CONST","context":"normal","value":"const","line":18,"start":316,"end":321},{"type":"T_IDENTIFIER","context":"normal","value":"otherProps","line":18,"start":322,"end":332},{"type":"T_COLON","context":"type","value":":","line":18,"start":332,"end":333},{"type":"T_IDENTIFIER","context":"type","value":"$PropertyType","line":18,"start":334,"end":347},{"type":"T_LESS_THAN","context":"type","value":"<","line":18,"start":347,"end":348},{"type":"T_IDENTIFIER","context":"type","value":"Tooltip","line":18,"start":348,"end":355},{"type":"T_COMMA","context":"type","value":",","line":18,"start":355,"end":356},{"type":"T_STRING","context":"type","value":"'props'","line":18,"start":357,"end":364},{"type":"T_GREATER_THAN","context":"type","value":">","line":18,"start":364,"end":365},{"type":"T_ASSIGN","context":"normal","value":"=","line":18,"start":366,"end":367},{"type":"T_LCURLY","context":"normal","value":"{","line":18,"start":368,"end":369},{"type":"T_IDENTIFIER","context":"normal","value":"text","line":19,"start":372,"end":376},{"type":"T_COLON","context":"normal","value":":","line":19,"start":376,"end":377},{"type":"T_STRING","context":"normal","value":"'foo'","line":19,"start":378,"end":383},{"type":"Line","context":"comment","value":"// Missing the `onMouseOver` definition","line":20,"start":386,"end":425},{"type":"T_RCURLY","context":"normal","value":"}","line":21,"start":426,"end":427},{"type":"T_SEMICOLON","context":"normal","value":";","line":21,"start":427,"end":428}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot assign object literal to `otherProps` because property `onMouseOver` is missing in object literal [1] but exists in `Props` [2]. [prop-missing]","context":"const otherProps: $PropertyType<Tooltip, 'props'> = {","source":"-","start":{"line":18,"column":53,"offset":368},"end":{"line":21,"column":1,"offset":427}}],"operation":null}]}
You can even nest lookups:
1
2
3
4
|
type PositionHandler = $PropertyType<$PropertyType<Tooltip, 'props'>, 'onMouseOver'>;
const handler: PositionHandler = (data: {x: number, y: number}) => undefined;
const handler2: PositionHandler = (data: string) => undefined;
|
Cannot resolve name `Tooltip`. [cannot-resolve-name]
{"value":"// @flow\ntype PositionHandler = $PropertyType<$PropertyType<Tooltip, 'props'>, 'onMouseOver'>;\nconst handler: PositionHandler = (data: {x: number, y: number}) => undefined;\nconst handler2: PositionHandler = (data: string) => undefined; // wrong parameter types\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":"PositionHandler","line":2,"start":14,"end":29},{"type":"T_ASSIGN","context":"type","value":"=","line":2,"start":30,"end":31},{"type":"T_IDENTIFIER","context":"type","value":"$PropertyType","line":2,"start":32,"end":45},{"type":"T_LESS_THAN","context":"type","value":"<","line":2,"start":45,"end":46},{"type":"T_IDENTIFIER","context":"type","value":"$PropertyType","line":2,"start":46,"end":59},{"type":"T_LESS_THAN","context":"type","value":"<","line":2,"start":59,"end":60},{"type":"T_IDENTIFIER","context":"type","value":"Tooltip","line":2,"start":60,"end":67},{"type":"T_COMMA","context":"type","value":",","line":2,"start":67,"end":68},{"type":"T_STRING","context":"type","value":"'props'","line":2,"start":69,"end":76},{"type":"T_GREATER_THAN","context":"type","value":">","line":2,"start":76,"end":77},{"type":"T_COMMA","context":"type","value":",","line":2,"start":77,"end":78},{"type":"T_STRING","context":"type","value":"'onMouseOver'","line":2,"start":79,"end":92},{"type":"T_GREATER_THAN","context":"type","value":">","line":2,"start":92,"end":93},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":93,"end":94},{"type":"T_CONST","context":"normal","value":"const","line":3,"start":95,"end":100},{"type":"T_IDENTIFIER","context":"normal","value":"handler","line":3,"start":101,"end":108},{"type":"T_COLON","context":"type","value":":","line":3,"start":108,"end":109},{"type":"T_IDENTIFIER","context":"type","value":"PositionHandler","line":3,"start":110,"end":125},{"type":"T_ASSIGN","context":"normal","value":"=","line":3,"start":126,"end":127},{"type":"T_LPAREN","context":"normal","value":"(","line":3,"start":128,"end":129},{"type":"T_IDENTIFIER","context":"normal","value":"data","line":3,"start":129,"end":133},{"type":"T_COLON","context":"type","value":":","line":3,"start":133,"end":134},{"type":"T_LCURLY","context":"type","value":"{","line":3,"start":135,"end":136},{"type":"T_IDENTIFIER","context":"normal","value":"x","line":3,"start":136,"end":137},{"type":"T_COLON","context":"type","value":":","line":3,"start":137,"end":138},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":3,"start":139,"end":145},{"type":"T_COMMA","context":"type","value":",","line":3,"start":145,"end":146},{"type":"T_IDENTIFIER","context":"normal","value":"y","line":3,"start":147,"end":148},{"type":"T_COLON","context":"type","value":":","line":3,"start":148,"end":149},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":3,"start":150,"end":156},{"type":"T_RCURLY","context":"type","value":"}","line":3,"start":156,"end":157},{"type":"T_RPAREN","context":"normal","value":")","line":3,"start":157,"end":158},{"type":"T_ARROW","context":"normal","value":"=>","line":3,"start":159,"end":161},{"type":"T_IDENTIFIER","context":"normal","value":"undefined","line":3,"start":162,"end":171},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":171,"end":172},{"type":"T_CONST","context":"normal","value":"const","line":4,"start":173,"end":178},{"type":"T_IDENTIFIER","context":"normal","value":"handler2","line":4,"start":179,"end":187},{"type":"T_COLON","context":"type","value":":","line":4,"start":187,"end":188},{"type":"T_IDENTIFIER","context":"type","value":"PositionHandler","line":4,"start":189,"end":204},{"type":"T_ASSIGN","context":"normal","value":"=","line":4,"start":205,"end":206},{"type":"T_LPAREN","context":"normal","value":"(","line":4,"start":207,"end":208},{"type":"T_IDENTIFIER","context":"normal","value":"data","line":4,"start":208,"end":212},{"type":"T_COLON","context":"type","value":":","line":4,"start":212,"end":213},{"type":"T_STRING_TYPE","context":"type","value":"string","line":4,"start":214,"end":220},{"type":"T_RPAREN","context":"normal","value":")","line":4,"start":220,"end":221},{"type":"T_ARROW","context":"normal","value":"=>","line":4,"start":222,"end":224},{"type":"T_IDENTIFIER","context":"normal","value":"undefined","line":4,"start":225,"end":234},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":234,"end":235},{"type":"Line","context":"comment","value":"// wrong parameter types","line":4,"start":236,"end":260}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot resolve name `Tooltip`. [cannot-resolve-name]","context":"type PositionHandler = $PropertyType<$PropertyType<Tooltip, 'props'>, 'onMouseOver'>;","source":"-","start":{"line":2,"column":52,"offset":60},"end":{"line":2,"column":58,"offset":67}}],"operation":null}]}
You can use this in combination with Class<T>
to get static props:
1
2
3
4
5
6
7
8
|
class BackboneModel {
static idAttribute: string | false;
}
type ID = $PropertyType<Class<BackboneModel>, 'idAttribute'>;
const someID: ID = '1234';
const someBadID: ID = true;
|
Cannot assign `true` to `someBadID` because: [incompatible-type] Either boolean [1] is incompatible with string [2]. Or boolean [1] is incompatible with boolean literal `false` [3].
{"value":"// @flow\nclass BackboneModel {\n static idAttribute: string | false;\n}\n\ntype ID = $PropertyType<Class<BackboneModel>, 'idAttribute'>;\nconst someID: ID = '1234';\nconst someBadID: ID = true;\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_CLASS","context":"normal","value":"class","line":2,"start":9,"end":14},{"type":"T_IDENTIFIER","context":"normal","value":"BackboneModel","line":2,"start":15,"end":28},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":29,"end":30},{"type":"T_STATIC","context":"normal","value":"static","line":3,"start":33,"end":39},{"type":"T_IDENTIFIER","context":"normal","value":"idAttribute","line":3,"start":40,"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_BIT_OR","context":"type","value":"|","line":3,"start":60,"end":61},{"type":"T_FALSE","context":"type","value":"false","line":3,"start":62,"end":67},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":67,"end":68},{"type":"T_RCURLY","context":"normal","value":"}","line":4,"start":69,"end":70},{"type":"T_TYPE","context":"normal","value":"type","line":6,"start":72,"end":76},{"type":"T_IDENTIFIER","context":"type","value":"ID","line":6,"start":77,"end":79},{"type":"T_ASSIGN","context":"type","value":"=","line":6,"start":80,"end":81},{"type":"T_IDENTIFIER","context":"type","value":"$PropertyType","line":6,"start":82,"end":95},{"type":"T_LESS_THAN","context":"type","value":"<","line":6,"start":95,"end":96},{"type":"T_IDENTIFIER","context":"type","value":"Class","line":6,"start":96,"end":101},{"type":"T_LESS_THAN","context":"type","value":"<","line":6,"start":101,"end":102},{"type":"T_IDENTIFIER","context":"type","value":"BackboneModel","line":6,"start":102,"end":115},{"type":"T_GREATER_THAN","context":"type","value":">","line":6,"start":115,"end":116},{"type":"T_COMMA","context":"type","value":",","line":6,"start":116,"end":117},{"type":"T_STRING","context":"type","value":"'idAttribute'","line":6,"start":118,"end":131},{"type":"T_GREATER_THAN","context":"type","value":">","line":6,"start":131,"end":132},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":132,"end":133},{"type":"T_CONST","context":"normal","value":"const","line":7,"start":134,"end":139},{"type":"T_IDENTIFIER","context":"normal","value":"someID","line":7,"start":140,"end":146},{"type":"T_COLON","context":"type","value":":","line":7,"start":146,"end":147},{"type":"T_IDENTIFIER","context":"type","value":"ID","line":7,"start":148,"end":150},{"type":"T_ASSIGN","context":"normal","value":"=","line":7,"start":151,"end":152},{"type":"T_STRING","context":"normal","value":"'1234'","line":7,"start":153,"end":159},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":159,"end":160},{"type":"T_CONST","context":"normal","value":"const","line":8,"start":161,"end":166},{"type":"T_IDENTIFIER","context":"normal","value":"someBadID","line":8,"start":167,"end":176},{"type":"T_COLON","context":"type","value":":","line":8,"start":176,"end":177},{"type":"T_IDENTIFIER","context":"type","value":"ID","line":8,"start":178,"end":180},{"type":"T_ASSIGN","context":"normal","value":"=","line":8,"start":181,"end":182},{"type":"T_TRUE","context":"normal","value":"true","line":8,"start":183,"end":187},{"type":"T_SEMICOLON","context":"normal","value":";","line":8,"start":187,"end":188}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot assign `true` to `someBadID` because: [incompatible-type] Either boolean [1] is incompatible with string [2]. Or boolean [1] is incompatible with boolean literal `false` [3].","context":"const someBadID: ID = true;","source":"-","start":{"line":8,"column":23,"offset":183},"end":{"line":8,"column":26,"offset":187}}],"operation":null}]}
$ElementType<T, K>
$ElementType<T, K>
is the type that represents the type of every element inside an array, tuple or object type T
, that matches the given key type K
.
For example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
type Obj = {
name: string,
age: number,
}
('Jon': $ElementType<Obj, 'name'>);
(42: $ElementType<Obj, 'age'>);
(true: $ElementType<Obj, 'name'>);
(true: $ElementType<Obj, 'other'>);
type Tuple = [boolean, string];
(true: $ElementType<Tuple, 0>);
('foo': $ElementType<Tuple, 1>);
('bar': $ElementType<Tuple, 2>);
|
Cannot cast `true` to `$ElementType` because boolean [1] is incompatible with string [2]. [incompatible-cast]
Cannot instantiate `$ElementType` because property `other` is missing in `Obj` [1]. [prop-missing]
Cannot instantiate `$ElementType` because `Tuple` [1] only has 2 elements, so index 2 is out of bounds. [invalid-tuple-index]
{"value":"// @flow\n\n// Using objects:\ntype Obj = {\n name: string,\n age: number,\n}\n('Jon': $ElementType<Obj, 'name'>);\n(42: $ElementType<Obj, 'age'>);\n(true: $ElementType<Obj, 'name'>); // Nope, `name` is not a boolean\n(true: $ElementType<Obj, 'other'>); // Nope, property `other` is not in Obj\n\n// Using tuples:\ntype Tuple = [boolean, string];\n(true: $ElementType<Tuple, 0>);\n('foo': $ElementType<Tuple, 1>);\n('bar': $ElementType<Tuple, 2>); // Nope, can't access position 2\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"Line","context":"comment","value":"// Using objects:","line":3,"start":10,"end":27},{"type":"T_TYPE","context":"normal","value":"type","line":4,"start":28,"end":32},{"type":"T_IDENTIFIER","context":"type","value":"Obj","line":4,"start":33,"end":36},{"type":"T_ASSIGN","context":"type","value":"=","line":4,"start":37,"end":38},{"type":"T_LCURLY","context":"type","value":"{","line":4,"start":39,"end":40},{"type":"T_IDENTIFIER","context":"normal","value":"name","line":5,"start":43,"end":47},{"type":"T_COLON","context":"type","value":":","line":5,"start":47,"end":48},{"type":"T_STRING_TYPE","context":"type","value":"string","line":5,"start":49,"end":55},{"type":"T_COMMA","context":"type","value":",","line":5,"start":55,"end":56},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":6,"start":59,"end":62},{"type":"T_COLON","context":"type","value":":","line":6,"start":62,"end":63},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":6,"start":64,"end":70},{"type":"T_COMMA","context":"type","value":",","line":6,"start":70,"end":71},{"type":"T_RCURLY","context":"type","value":"}","line":7,"start":72,"end":73},{"type":"T_LPAREN","context":"normal","value":"(","line":8,"start":74,"end":75},{"type":"T_STRING","context":"normal","value":"'Jon'","line":8,"start":75,"end":80},{"type":"T_COLON","context":"type","value":":","line":8,"start":80,"end":81},{"type":"T_IDENTIFIER","context":"type","value":"$ElementType","line":8,"start":82,"end":94},{"type":"T_LESS_THAN","context":"type","value":"<","line":8,"start":94,"end":95},{"type":"T_IDENTIFIER","context":"type","value":"Obj","line":8,"start":95,"end":98},{"type":"T_COMMA","context":"type","value":",","line":8,"start":98,"end":99},{"type":"T_STRING","context":"type","value":"'name'","line":8,"start":100,"end":106},{"type":"T_GREATER_THAN","context":"type","value":">","line":8,"start":106,"end":107},{"type":"T_RPAREN","context":"normal","value":")","line":8,"start":107,"end":108},{"type":"T_SEMICOLON","context":"normal","value":";","line":8,"start":108,"end":109},{"type":"T_LPAREN","context":"normal","value":"(","line":9,"start":110,"end":111},{"type":"T_NUMBER","context":"normal","value":"42","line":9,"start":111,"end":113},{"type":"T_COLON","context":"type","value":":","line":9,"start":113,"end":114},{"type":"T_IDENTIFIER","context":"type","value":"$ElementType","line":9,"start":115,"end":127},{"type":"T_LESS_THAN","context":"type","value":"<","line":9,"start":127,"end":128},{"type":"T_IDENTIFIER","context":"type","value":"Obj","line":9,"start":128,"end":131},{"type":"T_COMMA","context":"type","value":",","line":9,"start":131,"end":132},{"type":"T_STRING","context":"type","value":"'age'","line":9,"start":133,"end":138},{"type":"T_GREATER_THAN","context":"type","value":">","line":9,"start":138,"end":139},{"type":"T_RPAREN","context":"normal","value":")","line":9,"start":139,"end":140},{"type":"T_SEMICOLON","context":"normal","value":";","line":9,"start":140,"end":141},{"type":"T_LPAREN","context":"normal","value":"(","line":10,"start":142,"end":143},{"type":"T_TRUE","context":"normal","value":"true","line":10,"start":143,"end":147},{"type":"T_COLON","context":"type","value":":","line":10,"start":147,"end":148},{"type":"T_IDENTIFIER","context":"type","value":"$ElementType","line":10,"start":149,"end":161},{"type":"T_LESS_THAN","context":"type","value":"<","line":10,"start":161,"end":162},{"type":"T_IDENTIFIER","context":"type","value":"Obj","line":10,"start":162,"end":165},{"type":"T_COMMA","context":"type","value":",","line":10,"start":165,"end":166},{"type":"T_STRING","context":"type","value":"'name'","line":10,"start":167,"end":173},{"type":"T_GREATER_THAN","context":"type","value":">","line":10,"start":173,"end":174},{"type":"T_RPAREN","context":"normal","value":")","line":10,"start":174,"end":175},{"type":"T_SEMICOLON","context":"normal","value":";","line":10,"start":175,"end":176},{"type":"Line","context":"comment","value":"// Nope, `name` is not a boolean","line":10,"start":177,"end":209},{"type":"T_LPAREN","context":"normal","value":"(","line":11,"start":210,"end":211},{"type":"T_TRUE","context":"normal","value":"true","line":11,"start":211,"end":215},{"type":"T_COLON","context":"type","value":":","line":11,"start":215,"end":216},{"type":"T_IDENTIFIER","context":"type","value":"$ElementType","line":11,"start":217,"end":229},{"type":"T_LESS_THAN","context":"type","value":"<","line":11,"start":229,"end":230},{"type":"T_IDENTIFIER","context":"type","value":"Obj","line":11,"start":230,"end":233},{"type":"T_COMMA","context":"type","value":",","line":11,"start":233,"end":234},{"type":"T_STRING","context":"type","value":"'other'","line":11,"start":235,"end":242},{"type":"T_GREATER_THAN","context":"type","value":">","line":11,"start":242,"end":243},{"type":"T_RPAREN","context":"normal","value":")","line":11,"start":243,"end":244},{"type":"T_SEMICOLON","context":"normal","value":";","line":11,"start":244,"end":245},{"type":"Line","context":"comment","value":"// Nope, property `other` is not in Obj","line":11,"start":246,"end":285},{"type":"Line","context":"comment","value":"// Using tuples:","line":13,"start":287,"end":303},{"type":"T_TYPE","context":"normal","value":"type","line":14,"start":304,"end":308},{"type":"T_IDENTIFIER","context":"type","value":"Tuple","line":14,"start":309,"end":314},{"type":"T_ASSIGN","context":"type","value":"=","line":14,"start":315,"end":316},{"type":"T_LBRACKET","context":"type","value":"[","line":14,"start":317,"end":318},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":14,"start":318,"end":325},{"type":"T_COMMA","context":"type","value":",","line":14,"start":325,"end":326},{"type":"T_STRING_TYPE","context":"type","value":"string","line":14,"start":327,"end":333},{"type":"T_RBRACKET","context":"type","value":"]","line":14,"start":333,"end":334},{"type":"T_SEMICOLON","context":"normal","value":";","line":14,"start":334,"end":335},{"type":"T_LPAREN","context":"normal","value":"(","line":15,"start":336,"end":337},{"type":"T_TRUE","context":"normal","value":"true","line":15,"start":337,"end":341},{"type":"T_COLON","context":"type","value":":","line":15,"start":341,"end":342},{"type":"T_IDENTIFIER","context":"type","value":"$ElementType","line":15,"start":343,"end":355},{"type":"T_LESS_THAN","context":"type","value":"<","line":15,"start":355,"end":356},{"type":"T_IDENTIFIER","context":"type","value":"Tuple","line":15,"start":356,"end":361},{"type":"T_COMMA","context":"type","value":",","line":15,"start":361,"end":362},{"type":"T_NUMBER_SINGLETON_TYPE","context":"type","value":"0","line":15,"start":363,"end":364},{"type":"T_GREATER_THAN","context":"type","value":">","line":15,"start":364,"end":365},{"type":"T_RPAREN","context":"normal","value":")","line":15,"start":365,"end":366},{"type":"T_SEMICOLON","context":"normal","value":";","line":15,"start":366,"end":367},{"type":"T_LPAREN","context":"normal","value":"(","line":16,"start":368,"end":369},{"type":"T_STRING","context":"normal","value":"'foo'","line":16,"start":369,"end":374},{"type":"T_COLON","context":"type","value":":","line":16,"start":374,"end":375},{"type":"T_IDENTIFIER","context":"type","value":"$ElementType","line":16,"start":376,"end":388},{"type":"T_LESS_THAN","context":"type","value":"<","line":16,"start":388,"end":389},{"type":"T_IDENTIFIER","context":"type","value":"Tuple","line":16,"start":389,"end":394},{"type":"T_COMMA","context":"type","value":",","line":16,"start":394,"end":395},{"type":"T_NUMBER_SINGLETON_TYPE","context":"type","value":"1","line":16,"start":396,"end":397},{"type":"T_GREATER_THAN","context":"type","value":">","line":16,"start":397,"end":398},{"type":"T_RPAREN","context":"normal","value":")","line":16,"start":398,"end":399},{"type":"T_SEMICOLON","context":"normal","value":";","line":16,"start":399,"end":400},{"type":"T_LPAREN","context":"normal","value":"(","line":17,"start":401,"end":402},{"type":"T_STRING","context":"normal","value":"'bar'","line":17,"start":402,"end":407},{"type":"T_COLON","context":"type","value":":","line":17,"start":407,"end":408},{"type":"T_IDENTIFIER","context":"type","value":"$ElementType","line":17,"start":409,"end":421},{"type":"T_LESS_THAN","context":"type","value":"<","line":17,"start":421,"end":422},{"type":"T_IDENTIFIER","context":"type","value":"Tuple","line":17,"start":422,"end":427},{"type":"T_COMMA","context":"type","value":",","line":17,"start":427,"end":428},{"type":"T_NUMBER_SINGLETON_TYPE","context":"type","value":"2","line":17,"start":429,"end":430},{"type":"T_GREATER_THAN","context":"type","value":">","line":17,"start":430,"end":431},{"type":"T_RPAREN","context":"normal","value":")","line":17,"start":431,"end":432},{"type":"T_SEMICOLON","context":"normal","value":";","line":17,"start":432,"end":433},{"type":"Line","context":"comment","value":"// Nope, can't access position 2","line":17,"start":434,"end":466}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot cast `true` to `$ElementType` because boolean [1] is incompatible with string [2]. [incompatible-cast]","context":"(true: $ElementType<Obj, 'name'>); // Nope, `name` is not a boolean","source":"-","start":{"line":10,"column":2,"offset":143},"end":{"line":10,"column":5,"offset":147}}],"operation":null},{"id":"E2","messages":[{"id":"E2M1","description":"Cannot instantiate `$ElementType` because property `other` is missing in `Obj` [1]. [prop-missing]","context":"(true: $ElementType<Obj, 'other'>); // Nope, property `other` is not in Obj","source":"-","start":{"line":11,"column":26,"offset":235},"end":{"line":11,"column":32,"offset":242}}],"operation":null},{"id":"E3","messages":[{"id":"E3M1","description":"Cannot instantiate `$ElementType` because `Tuple` [1] only has 2 elements, so index 2 is out of bounds. [invalid-tuple-index]","context":"('bar': $ElementType<Tuple, 2>); // Nope, can't access position 2","source":"-","start":{"line":17,"column":9,"offset":409},"end":{"line":17,"column":30,"offset":431}}],"operation":null}]}
In the above case, we’re using literal values as K
, similarly to $PropertyType<T, k>
. However, when using $ElementType<T, K>
, K
is allowed to be any type, as long as that type exists on the keys of T
. For example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
type Obj = { [key: string]: number };
(42: $ElementType<Obj, string>);
(42: $ElementType<Obj, boolean>);
(true: $ElementType<Obj, string>);
type Arr = Array<boolean>;
(true: $ElementType<Arr, number>);
(true: $ElementType<Arr, boolean>);
('foo': $ElementType<Arr, number>);
|
boolean [1] is incompatible with string [2]. [incompatible-type]
Cannot cast `true` to `$ElementType` because boolean [1] is incompatible with number [2]. [incompatible-cast]
Cannot instantiate `$ElementType` because boolean [1] is not an array index. [incompatible-use]
Cannot cast `'foo'` to `$ElementType` because string [1] is incompatible with boolean [2]. [incompatible-cast]
{"value":"// @flow\n\n// Using objects\ntype Obj = { [key: string]: number };\n(42: $ElementType<Obj, string>);\n(42: $ElementType<Obj, boolean>); // Nope, object keys aren't booleans\n(true: $ElementType<Obj, string>); // Nope, elements are numbers\n\n\n// Using arrays, we don't statically know the size of the array, so you can just use the `number` type as the key:\ntype Arr = Array<boolean>;\n(true: $ElementType<Arr, number>);\n(true: $ElementType<Arr, boolean>); // Nope, array indices aren't booleans\n('foo': $ElementType<Arr, number>); // Nope, elements are booleans\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"Line","context":"comment","value":"// Using objects","line":3,"start":10,"end":26},{"type":"T_TYPE","context":"normal","value":"type","line":4,"start":27,"end":31},{"type":"T_IDENTIFIER","context":"type","value":"Obj","line":4,"start":32,"end":35},{"type":"T_ASSIGN","context":"type","value":"=","line":4,"start":36,"end":37},{"type":"T_LCURLY","context":"type","value":"{","line":4,"start":38,"end":39},{"type":"T_LBRACKET","context":"type","value":"[","line":4,"start":40,"end":41},{"type":"T_IDENTIFIER","context":"type","value":"key","line":4,"start":41,"end":44},{"type":"T_COLON","context":"type","value":":","line":4,"start":44,"end":45},{"type":"T_STRING_TYPE","context":"type","value":"string","line":4,"start":46,"end":52},{"type":"T_RBRACKET","context":"type","value":"]","line":4,"start":52,"end":53},{"type":"T_COLON","context":"type","value":":","line":4,"start":53,"end":54},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":4,"start":55,"end":61},{"type":"T_RCURLY","context":"type","value":"}","line":4,"start":62,"end":63},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":63,"end":64},{"type":"T_LPAREN","context":"normal","value":"(","line":5,"start":65,"end":66},{"type":"T_NUMBER","context":"normal","value":"42","line":5,"start":66,"end":68},{"type":"T_COLON","context":"type","value":":","line":5,"start":68,"end":69},{"type":"T_IDENTIFIER","context":"type","value":"$ElementType","line":5,"start":70,"end":82},{"type":"T_LESS_THAN","context":"type","value":"<","line":5,"start":82,"end":83},{"type":"T_IDENTIFIER","context":"type","value":"Obj","line":5,"start":83,"end":86},{"type":"T_COMMA","context":"type","value":",","line":5,"start":86,"end":87},{"type":"T_STRING_TYPE","context":"type","value":"string","line":5,"start":88,"end":94},{"type":"T_GREATER_THAN","context":"type","value":">","line":5,"start":94,"end":95},{"type":"T_RPAREN","context":"normal","value":")","line":5,"start":95,"end":96},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":96,"end":97},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":98,"end":99},{"type":"T_NUMBER","context":"normal","value":"42","line":6,"start":99,"end":101},{"type":"T_COLON","context":"type","value":":","line":6,"start":101,"end":102},{"type":"T_IDENTIFIER","context":"type","value":"$ElementType","line":6,"start":103,"end":115},{"type":"T_LESS_THAN","context":"type","value":"<","line":6,"start":115,"end":116},{"type":"T_IDENTIFIER","context":"type","value":"Obj","line":6,"start":116,"end":119},{"type":"T_COMMA","context":"type","value":",","line":6,"start":119,"end":120},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":6,"start":121,"end":128},{"type":"T_GREATER_THAN","context":"type","value":">","line":6,"start":128,"end":129},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":129,"end":130},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":130,"end":131},{"type":"Line","context":"comment","value":"// Nope, object keys aren't booleans","line":6,"start":132,"end":168},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":169,"end":170},{"type":"T_TRUE","context":"normal","value":"true","line":7,"start":170,"end":174},{"type":"T_COLON","context":"type","value":":","line":7,"start":174,"end":175},{"type":"T_IDENTIFIER","context":"type","value":"$ElementType","line":7,"start":176,"end":188},{"type":"T_LESS_THAN","context":"type","value":"<","line":7,"start":188,"end":189},{"type":"T_IDENTIFIER","context":"type","value":"Obj","line":7,"start":189,"end":192},{"type":"T_COMMA","context":"type","value":",","line":7,"start":192,"end":193},{"type":"T_STRING_TYPE","context":"type","value":"string","line":7,"start":194,"end":200},{"type":"T_GREATER_THAN","context":"type","value":">","line":7,"start":200,"end":201},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":201,"end":202},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":202,"end":203},{"type":"Line","context":"comment","value":"// Nope, elements are numbers","line":7,"start":204,"end":233},{"type":"Line","context":"comment","value":"// Using arrays, we don't statically know the size of the array, so you can just use the `number` type as the key:","line":10,"start":236,"end":350},{"type":"T_TYPE","context":"normal","value":"type","line":11,"start":351,"end":355},{"type":"T_IDENTIFIER","context":"type","value":"Arr","line":11,"start":356,"end":359},{"type":"T_ASSIGN","context":"type","value":"=","line":11,"start":360,"end":361},{"type":"T_IDENTIFIER","context":"type","value":"Array","line":11,"start":362,"end":367},{"type":"T_LESS_THAN","context":"type","value":"<","line":11,"start":367,"end":368},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":11,"start":368,"end":375},{"type":"T_GREATER_THAN","context":"type","value":">","line":11,"start":375,"end":376},{"type":"T_SEMICOLON","context":"normal","value":";","line":11,"start":376,"end":377},{"type":"T_LPAREN","context":"normal","value":"(","line":12,"start":378,"end":379},{"type":"T_TRUE","context":"normal","value":"true","line":12,"start":379,"end":383},{"type":"T_COLON","context":"type","value":":","line":12,"start":383,"end":384},{"type":"T_IDENTIFIER","context":"type","value":"$ElementType","line":12,"start":385,"end":397},{"type":"T_LESS_THAN","context":"type","value":"<","line":12,"start":397,"end":398},{"type":"T_IDENTIFIER","context":"type","value":"Arr","line":12,"start":398,"end":401},{"type":"T_COMMA","context":"type","value":",","line":12,"start":401,"end":402},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":12,"start":403,"end":409},{"type":"T_GREATER_THAN","context":"type","value":">","line":12,"start":409,"end":410},{"type":"T_RPAREN","context":"normal","value":")","line":12,"start":410,"end":411},{"type":"T_SEMICOLON","context":"normal","value":";","line":12,"start":411,"end":412},{"type":"T_LPAREN","context":"normal","value":"(","line":13,"start":413,"end":414},{"type":"T_TRUE","context":"normal","value":"true","line":13,"start":414,"end":418},{"type":"T_COLON","context":"type","value":":","line":13,"start":418,"end":419},{"type":"T_IDENTIFIER","context":"type","value":"$ElementType","line":13,"start":420,"end":432},{"type":"T_LESS_THAN","context":"type","value":"<","line":13,"start":432,"end":433},{"type":"T_IDENTIFIER","context":"type","value":"Arr","line":13,"start":433,"end":436},{"type":"T_COMMA","context":"type","value":",","line":13,"start":436,"end":437},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":13,"start":438,"end":445},{"type":"T_GREATER_THAN","context":"type","value":">","line":13,"start":445,"end":446},{"type":"T_RPAREN","context":"normal","value":")","line":13,"start":446,"end":447},{"type":"T_SEMICOLON","context":"normal","value":";","line":13,"start":447,"end":448},{"type":"Line","context":"comment","value":"// Nope, array indices aren't booleans","line":13,"start":449,"end":487},{"type":"T_LPAREN","context":"normal","value":"(","line":14,"start":488,"end":489},{"type":"T_STRING","context":"normal","value":"'foo'","line":14,"start":489,"end":494},{"type":"T_COLON","context":"type","value":":","line":14,"start":494,"end":495},{"type":"T_IDENTIFIER","context":"type","value":"$ElementType","line":14,"start":496,"end":508},{"type":"T_LESS_THAN","context":"type","value":"<","line":14,"start":508,"end":509},{"type":"T_IDENTIFIER","context":"type","value":"Arr","line":14,"start":509,"end":512},{"type":"T_COMMA","context":"type","value":",","line":14,"start":512,"end":513},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":14,"start":514,"end":520},{"type":"T_GREATER_THAN","context":"type","value":">","line":14,"start":520,"end":521},{"type":"T_RPAREN","context":"normal","value":")","line":14,"start":521,"end":522},{"type":"T_SEMICOLON","context":"normal","value":";","line":14,"start":522,"end":523},{"type":"Line","context":"comment","value":"// Nope, elements are booleans","line":14,"start":524,"end":554}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"boolean [1] is incompatible with string [2]. [incompatible-type]","context":"(42: $ElementType<Obj, boolean>); // Nope, object keys aren't booleans","source":"-","start":{"line":6,"column":24,"offset":121},"end":{"line":6,"column":30,"offset":128}}],"operation":null},{"id":"E2","messages":[{"id":"E2M1","description":"Cannot cast `true` to `$ElementType` because boolean [1] is incompatible with number [2]. [incompatible-cast]","context":"(true: $ElementType<Obj, string>); // Nope, elements are numbers","source":"-","start":{"line":7,"column":2,"offset":170},"end":{"line":7,"column":5,"offset":174}}],"operation":null},{"id":"E3","messages":[{"id":"E3M1","description":"Cannot instantiate `$ElementType` because boolean [1] is not an array index. [incompatible-use]","context":"(true: $ElementType<Arr, boolean>); // Nope, array indices aren't booleans","source":"-","start":{"line":13,"column":8,"offset":420},"end":{"line":13,"column":33,"offset":446}}],"operation":null},{"id":"E4","messages":[{"id":"E4M1","description":"Cannot cast `'foo'` to `$ElementType` because string [1] is incompatible with boolean [2]. [incompatible-cast]","context":"('foo': $ElementType<Arr, number>); // Nope, elements are booleans","source":"-","start":{"line":14,"column":2,"offset":489},"end":{"line":14,"column":6,"offset":494}}],"operation":null}]}
You can also nest calls to $ElementType<T, K>
, which is useful when you need to access the types inside nested structures:
1
2
3
4
5
6
|
type NumberObj = {
nums: Array<number>,
};
(42: $ElementType<$ElementType<NumberObj, 'nums'>, number>);
|
{"value":"// @flow\ntype NumberObj = {\n nums: Array<number>,\n};\n\n(42: $ElementType<$ElementType<NumberObj, 'nums'>, number>);\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":"NumberObj","line":2,"start":14,"end":23},{"type":"T_ASSIGN","context":"type","value":"=","line":2,"start":24,"end":25},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":26,"end":27},{"type":"T_IDENTIFIER","context":"normal","value":"nums","line":3,"start":30,"end":34},{"type":"T_COLON","context":"type","value":":","line":3,"start":34,"end":35},{"type":"T_IDENTIFIER","context":"type","value":"Array","line":3,"start":36,"end":41},{"type":"T_LESS_THAN","context":"type","value":"<","line":3,"start":41,"end":42},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":3,"start":42,"end":48},{"type":"T_GREATER_THAN","context":"type","value":">","line":3,"start":48,"end":49},{"type":"T_COMMA","context":"type","value":",","line":3,"start":49,"end":50},{"type":"T_RCURLY","context":"type","value":"}","line":4,"start":51,"end":52},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":52,"end":53},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":55,"end":56},{"type":"T_NUMBER","context":"normal","value":"42","line":6,"start":56,"end":58},{"type":"T_COLON","context":"type","value":":","line":6,"start":58,"end":59},{"type":"T_IDENTIFIER","context":"type","value":"$ElementType","line":6,"start":60,"end":72},{"type":"T_LESS_THAN","context":"type","value":"<","line":6,"start":72,"end":73},{"type":"T_IDENTIFIER","context":"type","value":"$ElementType","line":6,"start":73,"end":85},{"type":"T_LESS_THAN","context":"type","value":"<","line":6,"start":85,"end":86},{"type":"T_IDENTIFIER","context":"type","value":"NumberObj","line":6,"start":86,"end":95},{"type":"T_COMMA","context":"type","value":",","line":6,"start":95,"end":96},{"type":"T_STRING","context":"type","value":"'nums'","line":6,"start":97,"end":103},{"type":"T_GREATER_THAN","context":"type","value":">","line":6,"start":103,"end":104},{"type":"T_COMMA","context":"type","value":",","line":6,"start":104,"end":105},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":6,"start":106,"end":112},{"type":"T_GREATER_THAN","context":"type","value":">","line":6,"start":112,"end":113},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":113,"end":114},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":114,"end":115}],"errors":[]}
Additionally, one of the things that also makes $ElementType<T, K>
more powerful than $PropertyType<T, k>
is that you can use it with generics. For example:
1
2
3
4
5
6
7
8
|
function getProp<O: {+[string]: mixed}, P: $Keys<O>>(o: O, p: P): $ElementType<O, P> {
return o[p];
}
(getProp({a: 42}, 'a'): number);
(getProp({a: 42}, 'a'): string);
getProp({a: 42}, 'b');
|
Cannot cast `getProp(...)` to string because number [1] is incompatible with string [2]. [incompatible-cast]
Cannot call `getProp` with object literal bound to `o` because property `b` is missing in object literal [1]. [prop-missing]
Cannot call `getProp` with `'b'` bound to `p` because property `b` is missing in object literal [1]. [prop-missing]
{"value":"// @flow\nfunction getProp<O: {+[string]: mixed}, P: $Keys<O>>(o: O, p: P): $ElementType<O, P> {\n return o[p];\n}\n\n(getProp({a: 42}, 'a'): number); // OK\n(getProp({a: 42}, 'a'): string); // Error: number is not a string\ngetProp({a: 42}, 'b'); // Error: `b` does not exist\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":"getProp","line":2,"start":18,"end":25},{"type":"T_LESS_THAN","context":"type","value":"<","line":2,"start":25,"end":26},{"type":"T_IDENTIFIER","context":"type","value":"O","line":2,"start":26,"end":27},{"type":"T_COLON","context":"type","value":":","line":2,"start":27,"end":28},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":29,"end":30},{"type":"T_PLUS","context":"type","value":"+","line":2,"start":30,"end":31},{"type":"T_LBRACKET","context":"type","value":"[","line":2,"start":31,"end":32},{"type":"T_STRING_TYPE","context":"type","value":"string","line":2,"start":32,"end":38},{"type":"T_RBRACKET","context":"type","value":"]","line":2,"start":38,"end":39},{"type":"T_COLON","context":"type","value":":","line":2,"start":39,"end":40},{"type":"T_MIXED_TYPE","context":"type","value":"mixed","line":2,"start":41,"end":46},{"type":"T_RCURLY","context":"type","value":"}","line":2,"start":46,"end":47},{"type":"T_COMMA","context":"type","value":",","line":2,"start":47,"end":48},{"type":"T_IDENTIFIER","context":"type","value":"P","line":2,"start":49,"end":50},{"type":"T_COLON","context":"type","value":":","line":2,"start":50,"end":51},{"type":"T_IDENTIFIER","context":"type","value":"$Keys","line":2,"start":52,"end":57},{"type":"T_LESS_THAN","context":"type","value":"<","line":2,"start":57,"end":58},{"type":"T_IDENTIFIER","context":"type","value":"O","line":2,"start":58,"end":59},{"type":"T_GREATER_THAN","context":"type","value":">","line":2,"start":59,"end":60},{"type":"T_GREATER_THAN","context":"type","value":">","line":2,"start":60,"end":61},{"type":"T_LPAREN","context":"normal","value":"(","line":2,"start":61,"end":62},{"type":"T_IDENTIFIER","context":"normal","value":"o","line":2,"start":62,"end":63},{"type":"T_COLON","context":"type","value":":","line":2,"start":63,"end":64},{"type":"T_IDENTIFIER","context":"type","value":"O","line":2,"start":65,"end":66},{"type":"T_COMMA","context":"normal","value":",","line":2,"start":66,"end":67},{"type":"T_IDENTIFIER","context":"normal","value":"p","line":2,"start":68,"end":69},{"type":"T_COLON","context":"type","value":":","line":2,"start":69,"end":70},{"type":"T_IDENTIFIER","context":"type","value":"P","line":2,"start":71,"end":72},{"type":"T_RPAREN","context":"normal","value":")","line":2,"start":72,"end":73},{"type":"T_COLON","context":"type","value":":","line":2,"start":73,"end":74},{"type":"T_IDENTIFIER","context":"type","value":"$ElementType","line":2,"start":75,"end":87},{"type":"T_LESS_THAN","context":"type","value":"<","line":2,"start":87,"end":88},{"type":"T_IDENTIFIER","context":"type","value":"O","line":2,"start":88,"end":89},{"type":"T_COMMA","context":"type","value":",","line":2,"start":89,"end":90},{"type":"T_IDENTIFIER","context":"type","value":"P","line":2,"start":91,"end":92},{"type":"T_GREATER_THAN","context":"type","value":">","line":2,"start":92,"end":93},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":94,"end":95},{"type":"T_RETURN","context":"normal","value":"return","line":3,"start":98,"end":104},{"type":"T_IDENTIFIER","context":"normal","value":"o","line":3,"start":105,"end":106},{"type":"T_LBRACKET","context":"normal","value":"[","line":3,"start":106,"end":107},{"type":"T_IDENTIFIER","context":"normal","value":"p","line":3,"start":107,"end":108},{"type":"T_RBRACKET","context":"normal","value":"]","line":3,"start":108,"end":109},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":109,"end":110},{"type":"T_RCURLY","context":"normal","value":"}","line":4,"start":111,"end":112},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":114,"end":115},{"type":"T_IDENTIFIER","context":"normal","value":"getProp","line":6,"start":115,"end":122},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":122,"end":123},{"type":"T_LCURLY","context":"normal","value":"{","line":6,"start":123,"end":124},{"type":"T_IDENTIFIER","context":"normal","value":"a","line":6,"start":124,"end":125},{"type":"T_COLON","context":"normal","value":":","line":6,"start":125,"end":126},{"type":"T_NUMBER","context":"normal","value":"42","line":6,"start":127,"end":129},{"type":"T_RCURLY","context":"normal","value":"}","line":6,"start":129,"end":130},{"type":"T_COMMA","context":"normal","value":",","line":6,"start":130,"end":131},{"type":"T_STRING","context":"normal","value":"'a'","line":6,"start":132,"end":135},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":135,"end":136},{"type":"T_COLON","context":"type","value":":","line":6,"start":136,"end":137},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":6,"start":138,"end":144},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":144,"end":145},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":145,"end":146},{"type":"Line","context":"comment","value":"// OK","line":6,"start":147,"end":152},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":153,"end":154},{"type":"T_IDENTIFIER","context":"normal","value":"getProp","line":7,"start":154,"end":161},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":161,"end":162},{"type":"T_LCURLY","context":"normal","value":"{","line":7,"start":162,"end":163},{"type":"T_IDENTIFIER","context":"normal","value":"a","line":7,"start":163,"end":164},{"type":"T_COLON","context":"normal","value":":","line":7,"start":164,"end":165},{"type":"T_NUMBER","context":"normal","value":"42","line":7,"start":166,"end":168},{"type":"T_RCURLY","context":"normal","value":"}","line":7,"start":168,"end":169},{"type":"T_COMMA","context":"normal","value":",","line":7,"start":169,"end":170},{"type":"T_STRING","context":"normal","value":"'a'","line":7,"start":171,"end":174},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":174,"end":175},{"type":"T_COLON","context":"type","value":":","line":7,"start":175,"end":176},{"type":"T_STRING_TYPE","context":"type","value":"string","line":7,"start":177,"end":183},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":183,"end":184},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":184,"end":185},{"type":"Line","context":"comment","value":"// Error: number is not a string","line":7,"start":186,"end":218},{"type":"T_IDENTIFIER","context":"normal","value":"getProp","line":8,"start":219,"end":226},{"type":"T_LPAREN","context":"normal","value":"(","line":8,"start":226,"end":227},{"type":"T_LCURLY","context":"normal","value":"{","line":8,"start":227,"end":228},{"type":"T_IDENTIFIER","context":"normal","value":"a","line":8,"start":228,"end":229},{"type":"T_COLON","context":"normal","value":":","line":8,"start":229,"end":230},{"type":"T_NUMBER","context":"normal","value":"42","line":8,"start":231,"end":233},{"type":"T_RCURLY","context":"normal","value":"}","line":8,"start":233,"end":234},{"type":"T_COMMA","context":"normal","value":",","line":8,"start":234,"end":235},{"type":"T_STRING","context":"normal","value":"'b'","line":8,"start":236,"end":239},{"type":"T_RPAREN","context":"normal","value":")","line":8,"start":239,"end":240},{"type":"T_SEMICOLON","context":"normal","value":";","line":8,"start":240,"end":241},{"type":"Line","context":"comment","value":"// Error: `b` does not exist","line":8,"start":242,"end":270}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot cast `getProp(...)` to string because number [1] is incompatible with string [2]. [incompatible-cast]","context":"(getProp({a: 42}, 'a'): string); // Error: number is not a string","source":"-","start":{"line":7,"column":2,"offset":154},"end":{"line":7,"column":22,"offset":175}}],"operation":null},{"id":"E2","messages":[{"id":"E2M1","description":"Cannot call `getProp` with object literal bound to `o` because property `b` is missing in object literal [1]. [prop-missing]","context":"getProp({a: 42}, 'b'); // Error: `b` does not exist","source":"-","start":{"line":8,"column":9,"offset":227},"end":{"line":8,"column":15,"offset":234}}],"operation":null},{"id":"E3","messages":[{"id":"E3M1","description":"Cannot call `getProp` with `'b'` bound to `p` because property `b` is missing in object literal [1]. [prop-missing]","context":"getProp({a: 42}, 'b'); // Error: `b` does not exist","source":"-","start":{"line":8,"column":18,"offset":236},"end":{"line":8,"column":20,"offset":239}}],"operation":null}]}
$NonMaybeType<T>
$NonMaybeType<T>
converts a type T
to a non-maybe type. In other words, the values of $NonMaybeType<T>
are the values of T
except for null
and undefined
.
1
2
3
4
5
6
7
8
|
type MaybeName = ?string;
type Name = $NonMaybeType<MaybeName>;
('Gabriel': MaybeName);
(null: MaybeName);
('Gabriel': Name);
(null: Name);
|
Cannot cast `null` to `Name` because null [1] is incompatible with string [2]. [incompatible-cast]
{"value":"// @flow\ntype MaybeName = ?string;\ntype Name = $NonMaybeType<MaybeName>;\n\n('Gabriel': MaybeName); // Ok\n(null: MaybeName); // Ok\n('Gabriel': Name); // Ok\n(null: Name); // Error! null can't be annotated as Name because Name is not a maybe type\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":"MaybeName","line":2,"start":14,"end":23},{"type":"T_ASSIGN","context":"type","value":"=","line":2,"start":24,"end":25},{"type":"T_PLING","context":"type","value":"?","line":2,"start":26,"end":27},{"type":"T_STRING_TYPE","context":"type","value":"string","line":2,"start":27,"end":33},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":33,"end":34},{"type":"T_TYPE","context":"normal","value":"type","line":3,"start":35,"end":39},{"type":"T_IDENTIFIER","context":"type","value":"Name","line":3,"start":40,"end":44},{"type":"T_ASSIGN","context":"type","value":"=","line":3,"start":45,"end":46},{"type":"T_IDENTIFIER","context":"type","value":"$NonMaybeType","line":3,"start":47,"end":60},{"type":"T_LESS_THAN","context":"type","value":"<","line":3,"start":60,"end":61},{"type":"T_IDENTIFIER","context":"type","value":"MaybeName","line":3,"start":61,"end":70},{"type":"T_GREATER_THAN","context":"type","value":">","line":3,"start":70,"end":71},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":71,"end":72},{"type":"T_LPAREN","context":"normal","value":"(","line":5,"start":74,"end":75},{"type":"T_STRING","context":"normal","value":"'Gabriel'","line":5,"start":75,"end":84},{"type":"T_COLON","context":"type","value":":","line":5,"start":84,"end":85},{"type":"T_IDENTIFIER","context":"type","value":"MaybeName","line":5,"start":86,"end":95},{"type":"T_RPAREN","context":"normal","value":")","line":5,"start":95,"end":96},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":96,"end":97},{"type":"Line","context":"comment","value":"// Ok","line":5,"start":98,"end":103},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":104,"end":105},{"type":"T_NULL","context":"normal","value":"null","line":6,"start":105,"end":109},{"type":"T_COLON","context":"type","value":":","line":6,"start":109,"end":110},{"type":"T_IDENTIFIER","context":"type","value":"MaybeName","line":6,"start":111,"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":"// Ok","line":6,"start":123,"end":128},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":129,"end":130},{"type":"T_STRING","context":"normal","value":"'Gabriel'","line":7,"start":130,"end":139},{"type":"T_COLON","context":"type","value":":","line":7,"start":139,"end":140},{"type":"T_IDENTIFIER","context":"type","value":"Name","line":7,"start":141,"end":145},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":145,"end":146},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":146,"end":147},{"type":"Line","context":"comment","value":"// Ok","line":7,"start":148,"end":153},{"type":"T_LPAREN","context":"normal","value":"(","line":8,"start":154,"end":155},{"type":"T_NULL","context":"normal","value":"null","line":8,"start":155,"end":159},{"type":"T_COLON","context":"type","value":":","line":8,"start":159,"end":160},{"type":"T_IDENTIFIER","context":"type","value":"Name","line":8,"start":161,"end":165},{"type":"T_RPAREN","context":"normal","value":")","line":8,"start":165,"end":166},{"type":"T_SEMICOLON","context":"normal","value":";","line":8,"start":166,"end":167},{"type":"Line","context":"comment","value":"// Error! null can't be annotated as Name because Name is not a maybe type","line":8,"start":168,"end":242}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot cast `null` to `Name` because null [1] is incompatible with string [2]. [incompatible-cast]","context":"(null: Name); // Error! null can't be annotated as Name because Name is not a maybe type","source":"-","start":{"line":8,"column":2,"offset":155},"end":{"line":8,"column":5,"offset":159}}],"operation":null}]}
$ObjMap<T, F>
ObjMap<T, F>
takes an object type T
, and a function type F
, and returns the object type obtained by mapping the type of each value in the object with the provided function type F
. In other words, $ObjMap
will call (at the type level) the given function type F
for every property value type in T
, and return the resulting object type from those calls.
Let’s see an example. Suppose you have a function called run
that takes an object of thunks (functions in the form () => A
) as input:
1
2
3
4
|
function run<O: {[key: string]: Function}>(o: O) {
return Object.keys(o).reduce((acc, k) => Object.assign(acc, { [k]: o[k]() }), {});
}
|
{"value":"// @flow\nfunction run<O: {[key: string]: Function}>(o: O) {\n return Object.keys(o).reduce((acc, k) => Object.assign(acc, { [k]: o[k]() }), {});\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":"run","line":2,"start":18,"end":21},{"type":"T_LESS_THAN","context":"type","value":"<","line":2,"start":21,"end":22},{"type":"T_IDENTIFIER","context":"type","value":"O","line":2,"start":22,"end":23},{"type":"T_COLON","context":"type","value":":","line":2,"start":23,"end":24},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":25,"end":26},{"type":"T_LBRACKET","context":"type","value":"[","line":2,"start":26,"end":27},{"type":"T_IDENTIFIER","context":"type","value":"key","line":2,"start":27,"end":30},{"type":"T_COLON","context":"type","value":":","line":2,"start":30,"end":31},{"type":"T_STRING_TYPE","context":"type","value":"string","line":2,"start":32,"end":38},{"type":"T_RBRACKET","context":"type","value":"]","line":2,"start":38,"end":39},{"type":"T_COLON","context":"type","value":":","line":2,"start":39,"end":40},{"type":"T_IDENTIFIER","context":"type","value":"Function","line":2,"start":41,"end":49},{"type":"T_RCURLY","context":"type","value":"}","line":2,"start":49,"end":50},{"type":"T_GREATER_THAN","context":"type","value":">","line":2,"start":50,"end":51},{"type":"T_LPAREN","context":"normal","value":"(","line":2,"start":51,"end":52},{"type":"T_IDENTIFIER","context":"normal","value":"o","line":2,"start":52,"end":53},{"type":"T_COLON","context":"type","value":":","line":2,"start":53,"end":54},{"type":"T_IDENTIFIER","context":"type","value":"O","line":2,"start":55,"end":56},{"type":"T_RPAREN","context":"normal","value":")","line":2,"start":56,"end":57},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":58,"end":59},{"type":"T_RETURN","context":"normal","value":"return","line":3,"start":62,"end":68},{"type":"T_IDENTIFIER","context":"normal","value":"Object","line":3,"start":69,"end":75},{"type":"T_PERIOD","context":"normal","value":".","line":3,"start":75,"end":76},{"type":"T_IDENTIFIER","context":"normal","value":"keys","line":3,"start":76,"end":80},{"type":"T_LPAREN","context":"normal","value":"(","line":3,"start":80,"end":81},{"type":"T_IDENTIFIER","context":"normal","value":"o","line":3,"start":81,"end":82},{"type":"T_RPAREN","context":"normal","value":")","line":3,"start":82,"end":83},{"type":"T_PERIOD","context":"normal","value":".","line":3,"start":83,"end":84},{"type":"T_IDENTIFIER","context":"normal","value":"reduce","line":3,"start":84,"end":90},{"type":"T_LPAREN","context":"normal","value":"(","line":3,"start":90,"end":91},{"type":"T_LPAREN","context":"normal","value":"(","line":3,"start":91,"end":92},{"type":"T_IDENTIFIER","context":"normal","value":"acc","line":3,"start":92,"end":95},{"type":"T_COMMA","context":"normal","value":",","line":3,"start":95,"end":96},{"type":"T_IDENTIFIER","context":"normal","value":"k","line":3,"start":97,"end":98},{"type":"T_RPAREN","context":"normal","value":")","line":3,"start":98,"end":99},{"type":"T_ARROW","context":"normal","value":"=>","line":3,"start":100,"end":102},{"type":"T_IDENTIFIER","context":"normal","value":"Object","line":3,"start":103,"end":109},{"type":"T_PERIOD","context":"normal","value":".","line":3,"start":109,"end":110},{"type":"T_IDENTIFIER","context":"normal","value":"assign","line":3,"start":110,"end":116},{"type":"T_LPAREN","context":"normal","value":"(","line":3,"start":116,"end":117},{"type":"T_IDENTIFIER","context":"normal","value":"acc","line":3,"start":117,"end":120},{"type":"T_COMMA","context":"normal","value":",","line":3,"start":120,"end":121},{"type":"T_LCURLY","context":"normal","value":"{","line":3,"start":122,"end":123},{"type":"T_LBRACKET","context":"normal","value":"[","line":3,"start":124,"end":125},{"type":"T_IDENTIFIER","context":"normal","value":"k","line":3,"start":125,"end":126},{"type":"T_RBRACKET","context":"normal","value":"]","line":3,"start":126,"end":127},{"type":"T_COLON","context":"normal","value":":","line":3,"start":127,"end":128},{"type":"T_IDENTIFIER","context":"normal","value":"o","line":3,"start":129,"end":130},{"type":"T_LBRACKET","context":"normal","value":"[","line":3,"start":130,"end":131},{"type":"T_IDENTIFIER","context":"normal","value":"k","line":3,"start":131,"end":132},{"type":"T_RBRACKET","context":"normal","value":"]","line":3,"start":132,"end":133},{"type":"T_LPAREN","context":"normal","value":"(","line":3,"start":133,"end":134},{"type":"T_RPAREN","context":"normal","value":")","line":3,"start":134,"end":135},{"type":"T_RCURLY","context":"normal","value":"}","line":3,"start":136,"end":137},{"type":"T_RPAREN","context":"normal","value":")","line":3,"start":137,"end":138},{"type":"T_COMMA","context":"normal","value":",","line":3,"start":138,"end":139},{"type":"T_LCURLY","context":"normal","value":"{","line":3,"start":140,"end":141},{"type":"T_RCURLY","context":"normal","value":"}","line":3,"start":141,"end":142},{"type":"T_RPAREN","context":"normal","value":")","line":3,"start":142,"end":143},{"type":"T_SEMICOLON","context":"normal","value":";","line":3,"start":143,"end":144},{"type":"T_RCURLY","context":"normal","value":"}","line":4,"start":145,"end":146}],"errors":[]}
The function’s purpose is to run all the thunks and return an object made of values. What’s the return type of this function?
The keys are the same, but the values have a different type, namely the return type of each function. At a value level (the implementation of the function) we’re essentially mapping over the object to produce new values for the keys. How to express this at a type level?
This is where ObjMap<T, F>
comes in handy.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
type ExtractReturnType = <V>(() => V) => V;
declare function run<O: {[key: string]: Function}>(o: O): $ObjMap<O, ExtractReturnType>;
const o = {
a: () => true,
b: () => 'foo'
};
(run(o).a: boolean);
(run(o).b: string);
(run(o).b: boolean);
run(o).c;
|
Cannot cast `run(...).b` to boolean because string [1] is incompatible with boolean [2]. [incompatible-cast]
Cannot get `run(...).c` because property `c` is missing in object type [1]. [prop-missing]
{"value":"// @flow\n\n// let's write a function type that takes a `() => V` and returns a `V` (its return type)\ntype ExtractReturnType = <V>(() => V) => V;\n\ndeclare function run<O: {[key: string]: Function}>(o: O): $ObjMap<O, ExtractReturnType>;\n\nconst o = {\n a: () => true,\n b: () => 'foo'\n};\n\n(run(o).a: boolean); // Ok\n(run(o).b: string); // Ok\n// $ExpectError\n(run(o).b: boolean); // Nope, b is a string\n// $ExpectError\nrun(o).c; // Nope, c was not in the original object\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"Line","context":"comment","value":"// let's write a function type that takes a `() => V` and returns a `V` (its return type)","line":3,"start":10,"end":99},{"type":"T_TYPE","context":"normal","value":"type","line":4,"start":100,"end":104},{"type":"T_IDENTIFIER","context":"type","value":"ExtractReturnType","line":4,"start":105,"end":122},{"type":"T_ASSIGN","context":"type","value":"=","line":4,"start":123,"end":124},{"type":"T_LESS_THAN","context":"type","value":"<","line":4,"start":125,"end":126},{"type":"T_IDENTIFIER","context":"type","value":"V","line":4,"start":126,"end":127},{"type":"T_GREATER_THAN","context":"type","value":">","line":4,"start":127,"end":128},{"type":"T_LPAREN","context":"type","value":"(","line":4,"start":128,"end":129},{"type":"T_LPAREN","context":"type","value":"(","line":4,"start":129,"end":130},{"type":"T_RPAREN","context":"type","value":")","line":4,"start":130,"end":131},{"type":"T_ARROW","context":"type","value":"=>","line":4,"start":132,"end":134},{"type":"T_IDENTIFIER","context":"type","value":"V","line":4,"start":135,"end":136},{"type":"T_RPAREN","context":"type","value":")","line":4,"start":136,"end":137},{"type":"T_ARROW","context":"type","value":"=>","line":4,"start":138,"end":140},{"type":"T_IDENTIFIER","context":"type","value":"V","line":4,"start":141,"end":142},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":142,"end":143},{"type":"T_DECLARE","context":"normal","value":"declare","line":6,"start":145,"end":152},{"type":"T_FUNCTION","context":"normal","value":"function","line":6,"start":153,"end":161},{"type":"T_IDENTIFIER","context":"normal","value":"run","line":6,"start":162,"end":165},{"type":"T_LESS_THAN","context":"type","value":"<","line":6,"start":165,"end":166},{"type":"T_IDENTIFIER","context":"type","value":"O","line":6,"start":166,"end":167},{"type":"T_COLON","context":"type","value":":","line":6,"start":167,"end":168},{"type":"T_LCURLY","context":"type","value":"{","line":6,"start":169,"end":170},{"type":"T_LBRACKET","context":"type","value":"[","line":6,"start":170,"end":171},{"type":"T_IDENTIFIER","context":"type","value":"key","line":6,"start":171,"end":174},{"type":"T_COLON","context":"type","value":":","line":6,"start":174,"end":175},{"type":"T_STRING_TYPE","context":"type","value":"string","line":6,"start":176,"end":182},{"type":"T_RBRACKET","context":"type","value":"]","line":6,"start":182,"end":183},{"type":"T_COLON","context":"type","value":":","line":6,"start":183,"end":184},{"type":"T_IDENTIFIER","context":"type","value":"Function","line":6,"start":185,"end":193},{"type":"T_RCURLY","context":"type","value":"}","line":6,"start":193,"end":194},{"type":"T_GREATER_THAN","context":"type","value":">","line":6,"start":194,"end":195},{"type":"T_LPAREN","context":"type","value":"(","line":6,"start":195,"end":196},{"type":"T_IDENTIFIER","context":"normal","value":"o","line":6,"start":196,"end":197},{"type":"T_COLON","context":"type","value":":","line":6,"start":197,"end":198},{"type":"T_IDENTIFIER","context":"type","value":"O","line":6,"start":199,"end":200},{"type":"T_RPAREN","context":"type","value":")","line":6,"start":200,"end":201},{"type":"T_COLON","context":"normal","value":":","line":6,"start":201,"end":202},{"type":"T_IDENTIFIER","context":"type","value":"$ObjMap","line":6,"start":203,"end":210},{"type":"T_LESS_THAN","context":"type","value":"<","line":6,"start":210,"end":211},{"type":"T_IDENTIFIER","context":"type","value":"O","line":6,"start":211,"end":212},{"type":"T_COMMA","context":"type","value":",","line":6,"start":212,"end":213},{"type":"T_IDENTIFIER","context":"type","value":"ExtractReturnType","line":6,"start":214,"end":231},{"type":"T_GREATER_THAN","context":"type","value":">","line":6,"start":231,"end":232},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":232,"end":233},{"type":"T_CONST","context":"normal","value":"const","line":8,"start":235,"end":240},{"type":"T_IDENTIFIER","context":"normal","value":"o","line":8,"start":241,"end":242},{"type":"T_ASSIGN","context":"normal","value":"=","line":8,"start":243,"end":244},{"type":"T_LCURLY","context":"normal","value":"{","line":8,"start":245,"end":246},{"type":"T_IDENTIFIER","context":"normal","value":"a","line":9,"start":249,"end":250},{"type":"T_COLON","context":"normal","value":":","line":9,"start":250,"end":251},{"type":"T_LPAREN","context":"normal","value":"(","line":9,"start":252,"end":253},{"type":"T_RPAREN","context":"normal","value":")","line":9,"start":253,"end":254},{"type":"T_ARROW","context":"normal","value":"=>","line":9,"start":255,"end":257},{"type":"T_TRUE","context":"normal","value":"true","line":9,"start":258,"end":262},{"type":"T_COMMA","context":"normal","value":",","line":9,"start":262,"end":263},{"type":"T_IDENTIFIER","context":"normal","value":"b","line":10,"start":266,"end":267},{"type":"T_COLON","context":"normal","value":":","line":10,"start":267,"end":268},{"type":"T_LPAREN","context":"normal","value":"(","line":10,"start":269,"end":270},{"type":"T_RPAREN","context":"normal","value":")","line":10,"start":270,"end":271},{"type":"T_ARROW","context":"normal","value":"=>","line":10,"start":272,"end":274},{"type":"T_STRING","context":"normal","value":"'foo'","line":10,"start":275,"end":280},{"type":"T_RCURLY","context":"normal","value":"}","line":11,"start":281,"end":282},{"type":"T_SEMICOLON","context":"normal","value":";","line":11,"start":282,"end":283},{"type":"T_LPAREN","context":"normal","value":"(","line":13,"start":285,"end":286},{"type":"T_IDENTIFIER","context":"normal","value":"run","line":13,"start":286,"end":289},{"type":"T_LPAREN","context":"normal","value":"(","line":13,"start":289,"end":290},{"type":"T_IDENTIFIER","context":"normal","value":"o","line":13,"start":290,"end":291},{"type":"T_RPAREN","context":"normal","value":")","line":13,"start":291,"end":292},{"type":"T_PERIOD","context":"normal","value":".","line":13,"start":292,"end":293},{"type":"T_IDENTIFIER","context":"normal","value":"a","line":13,"start":293,"end":294},{"type":"T_COLON","context":"type","value":":","line":13,"start":294,"end":295},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":13,"start":296,"end":303},{"type":"T_RPAREN","context":"normal","value":")","line":13,"start":303,"end":304},{"type":"T_SEMICOLON","context":"normal","value":";","line":13,"start":304,"end":305},{"type":"Line","context":"comment","value":"// Ok","line":13,"start":306,"end":311},{"type":"T_LPAREN","context":"normal","value":"(","line":14,"start":312,"end":313},{"type":"T_IDENTIFIER","context":"normal","value":"run","line":14,"start":313,"end":316},{"type":"T_LPAREN","context":"normal","value":"(","line":14,"start":316,"end":317},{"type":"T_IDENTIFIER","context":"normal","value":"o","line":14,"start":317,"end":318},{"type":"T_RPAREN","context":"normal","value":")","line":14,"start":318,"end":319},{"type":"T_PERIOD","context":"normal","value":".","line":14,"start":319,"end":320},{"type":"T_IDENTIFIER","context":"normal","value":"b","line":14,"start":320,"end":321},{"type":"T_COLON","context":"type","value":":","line":14,"start":321,"end":322},{"type":"T_STRING_TYPE","context":"type","value":"string","line":14,"start":323,"end":329},{"type":"T_RPAREN","context":"normal","value":")","line":14,"start":329,"end":330},{"type":"T_SEMICOLON","context":"normal","value":";","line":14,"start":330,"end":331},{"type":"Line","context":"comment","value":"// Ok","line":14,"start":333,"end":338},{"type":"Line","context":"comment","value":"// $ExpectError","line":15,"start":339,"end":354},{"type":"T_LPAREN","context":"normal","value":"(","line":16,"start":355,"end":356},{"type":"T_IDENTIFIER","context":"normal","value":"run","line":16,"start":356,"end":359},{"type":"T_LPAREN","context":"normal","value":"(","line":16,"start":359,"end":360},{"type":"T_IDENTIFIER","context":"normal","value":"o","line":16,"start":360,"end":361},{"type":"T_RPAREN","context":"normal","value":")","line":16,"start":361,"end":362},{"type":"T_PERIOD","context":"normal","value":".","line":16,"start":362,"end":363},{"type":"T_IDENTIFIER","context":"normal","value":"b","line":16,"start":363,"end":364},{"type":"T_COLON","context":"type","value":":","line":16,"start":364,"end":365},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":16,"start":366,"end":373},{"type":"T_RPAREN","context":"normal","value":")","line":16,"start":373,"end":374},{"type":"T_SEMICOLON","context":"normal","value":";","line":16,"start":374,"end":375},{"type":"Line","context":"comment","value":"// Nope, b is a string","line":16,"start":376,"end":398},{"type":"Line","context":"comment","value":"// $ExpectError","line":17,"start":399,"end":414},{"type":"T_IDENTIFIER","context":"normal","value":"run","line":18,"start":415,"end":418},{"type":"T_LPAREN","context":"normal","value":"(","line":18,"start":418,"end":419},{"type":"T_IDENTIFIER","context":"normal","value":"o","line":18,"start":419,"end":420},{"type":"T_RPAREN","context":"normal","value":")","line":18,"start":420,"end":421},{"type":"T_PERIOD","context":"normal","value":".","line":18,"start":421,"end":422},{"type":"T_IDENTIFIER","context":"normal","value":"c","line":18,"start":422,"end":423},{"type":"T_SEMICOLON","context":"normal","value":";","line":18,"start":423,"end":424},{"type":"Line","context":"comment","value":"// Nope, c was not in the original object","line":18,"start":436,"end":477}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot cast `run(...).b` to boolean because string [1] is incompatible with boolean [2]. [incompatible-cast]","context":"(run(o).b: boolean); // Nope, b is a string","source":"-","start":{"line":16,"column":2,"offset":356},"end":{"line":16,"column":9,"offset":364}}],"operation":null},{"id":"E2","messages":[{"id":"E2M1","description":"Cannot get `run(...).c` because property `c` is missing in object type [1]. [prop-missing]","context":"run(o).c; // Nope, c was not in the original object","source":"-","start":{"line":18,"column":8,"offset":422},"end":{"line":18,"column":8,"offset":423}}],"operation":null}]}
This is extremely useful for expressing the return type of functions that manipulate objects values. You could use a similar approach (for instance) to provide the return type of bluebird’s Promise.props
function, which is like Promise.all
but takes an object as input.
Here’s a possible declaration of this function, which is very similar to our first example:
1
2
|
declare function props<A, O: { [key: string]: A }>(promises: O): Promise<$ObjMap<O, typeof $await>>;
|
{"value":"// @flow\ndeclare function props<A, O: { [key: string]: A }>(promises: O): Promise<$ObjMap<O, typeof $await>>;\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_DECLARE","context":"normal","value":"declare","line":2,"start":9,"end":16},{"type":"T_FUNCTION","context":"normal","value":"function","line":2,"start":17,"end":25},{"type":"T_IDENTIFIER","context":"normal","value":"props","line":2,"start":26,"end":31},{"type":"T_LESS_THAN","context":"type","value":"<","line":2,"start":31,"end":32},{"type":"T_IDENTIFIER","context":"type","value":"A","line":2,"start":32,"end":33},{"type":"T_COMMA","context":"type","value":",","line":2,"start":33,"end":34},{"type":"T_IDENTIFIER","context":"type","value":"O","line":2,"start":35,"end":36},{"type":"T_COLON","context":"type","value":":","line":2,"start":36,"end":37},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":38,"end":39},{"type":"T_LBRACKET","context":"type","value":"[","line":2,"start":40,"end":41},{"type":"T_IDENTIFIER","context":"type","value":"key","line":2,"start":41,"end":44},{"type":"T_COLON","context":"type","value":":","line":2,"start":44,"end":45},{"type":"T_STRING_TYPE","context":"type","value":"string","line":2,"start":46,"end":52},{"type":"T_RBRACKET","context":"type","value":"]","line":2,"start":52,"end":53},{"type":"T_COLON","context":"type","value":":","line":2,"start":53,"end":54},{"type":"T_IDENTIFIER","context":"type","value":"A","line":2,"start":55,"end":56},{"type":"T_RCURLY","context":"type","value":"}","line":2,"start":57,"end":58},{"type":"T_GREATER_THAN","context":"type","value":">","line":2,"start":58,"end":59},{"type":"T_LPAREN","context":"type","value":"(","line":2,"start":59,"end":60},{"type":"T_IDENTIFIER","context":"normal","value":"promises","line":2,"start":60,"end":68},{"type":"T_COLON","context":"type","value":":","line":2,"start":68,"end":69},{"type":"T_IDENTIFIER","context":"type","value":"O","line":2,"start":70,"end":71},{"type":"T_RPAREN","context":"type","value":")","line":2,"start":71,"end":72},{"type":"T_COLON","context":"normal","value":":","line":2,"start":72,"end":73},{"type":"T_IDENTIFIER","context":"type","value":"Promise","line":2,"start":74,"end":81},{"type":"T_LESS_THAN","context":"type","value":"<","line":2,"start":81,"end":82},{"type":"T_IDENTIFIER","context":"type","value":"$ObjMap","line":2,"start":82,"end":89},{"type":"T_LESS_THAN","context":"type","value":"<","line":2,"start":89,"end":90},{"type":"T_IDENTIFIER","context":"type","value":"O","line":2,"start":90,"end":91},{"type":"T_COMMA","context":"type","value":",","line":2,"start":91,"end":92},{"type":"T_TYPEOF","context":"type","value":"typeof","line":2,"start":93,"end":99},{"type":"T_IDENTIFIER","context":"type","value":"$await","line":2,"start":100,"end":106},{"type":"T_GREATER_THAN","context":"type","value":">","line":2,"start":106,"end":107},{"type":"T_GREATER_THAN","context":"type","value":">","line":2,"start":107,"end":108},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":108,"end":109}],"errors":[]}
And use:
1
2
3
4
5
6
7
|
const promises = { a: Promise.resolve(42) };
props(promises).then(o => {
(o.a: 42);
(o.a: 43);
});
|
Cannot resolve name `props`. [cannot-resolve-name]
{"value":"// @flow\nconst promises = { a: Promise.resolve(42) };\nprops(promises).then(o => {\n (o.a: 42); // Ok\n // $ExpectError\n (o.a: 43); // Error, flow knows it's 42\n});\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_CONST","context":"normal","value":"const","line":2,"start":9,"end":14},{"type":"T_IDENTIFIER","context":"normal","value":"promises","line":2,"start":15,"end":23},{"type":"T_ASSIGN","context":"normal","value":"=","line":2,"start":24,"end":25},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":26,"end":27},{"type":"T_IDENTIFIER","context":"normal","value":"a","line":2,"start":28,"end":29},{"type":"T_COLON","context":"normal","value":":","line":2,"start":29,"end":30},{"type":"T_IDENTIFIER","context":"normal","value":"Promise","line":2,"start":31,"end":38},{"type":"T_PERIOD","context":"normal","value":".","line":2,"start":38,"end":39},{"type":"T_IDENTIFIER","context":"normal","value":"resolve","line":2,"start":39,"end":46},{"type":"T_LPAREN","context":"normal","value":"(","line":2,"start":46,"end":47},{"type":"T_NUMBER","context":"normal","value":"42","line":2,"start":47,"end":49},{"type":"T_RPAREN","context":"normal","value":")","line":2,"start":49,"end":50},{"type":"T_RCURLY","context":"normal","value":"}","line":2,"start":51,"end":52},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":52,"end":53},{"type":"T_IDENTIFIER","context":"normal","value":"props","line":3,"start":54,"end":59},{"type":"T_LPAREN","context":"normal","value":"(","line":3,"start":59,"end":60},{"type":"T_IDENTIFIER","context":"normal","value":"promises","line":3,"start":60,"end":68},{"type":"T_RPAREN","context":"normal","value":")","line":3,"start":68,"end":69},{"type":"T_PERIOD","context":"normal","value":".","line":3,"start":69,"end":70},{"type":"T_IDENTIFIER","context":"normal","value":"then","line":3,"start":70,"end":74},{"type":"T_LPAREN","context":"normal","value":"(","line":3,"start":74,"end":75},{"type":"T_IDENTIFIER","context":"normal","value":"o","line":3,"start":75,"end":76},{"type":"T_ARROW","context":"normal","value":"=>","line":3,"start":77,"end":79},{"type":"T_LCURLY","context":"normal","value":"{","line":3,"start":80,"end":81},{"type":"T_LPAREN","context":"normal","value":"(","line":4,"start":84,"end":85},{"type":"T_IDENTIFIER","context":"normal","value":"o","line":4,"start":85,"end":86},{"type":"T_PERIOD","context":"normal","value":".","line":4,"start":86,"end":87},{"type":"T_IDENTIFIER","context":"normal","value":"a","line":4,"start":87,"end":88},{"type":"T_COLON","context":"type","value":":","line":4,"start":88,"end":89},{"type":"T_NUMBER_SINGLETON_TYPE","context":"type","value":"42","line":4,"start":90,"end":92},{"type":"T_RPAREN","context":"normal","value":")","line":4,"start":92,"end":93},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":93,"end":94},{"type":"Line","context":"comment","value":"// Ok","line":4,"start":95,"end":100},{"type":"Line","context":"comment","value":"// $ExpectError","line":5,"start":103,"end":118},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":121,"end":122},{"type":"T_IDENTIFIER","context":"normal","value":"o","line":6,"start":122,"end":123},{"type":"T_PERIOD","context":"normal","value":".","line":6,"start":123,"end":124},{"type":"T_IDENTIFIER","context":"normal","value":"a","line":6,"start":124,"end":125},{"type":"T_COLON","context":"type","value":":","line":6,"start":125,"end":126},{"type":"T_NUMBER_SINGLETON_TYPE","context":"type","value":"43","line":6,"start":127,"end":129},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":129,"end":130},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":130,"end":131},{"type":"Line","context":"comment","value":"// Error, flow knows it's 42","line":6,"start":132,"end":160},{"type":"T_RCURLY","context":"normal","value":"}","line":7,"start":161,"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}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot resolve name `props`. [cannot-resolve-name]","context":"props(promises).then(o => {","source":"-","start":{"line":3,"column":1,"offset":54},"end":{"line":3,"column":5,"offset":59}}],"operation":null}]}
$ObjMapi<T, F>
ObjMapi<T, F>
is similar to ObjMap<T, F>
. The difference is that function
type F
will be called with both the key and value types of the elements of
the object type T
, instead of just the value types. For example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
const o = {
a: () => true,
b: () => 'foo'
};
type ExtractReturnObjectType = <K, V>(K, () => V) => { k: K, v: V };
declare function run<O: Object>(o: O): $ObjMapi<O, ExtractReturnObjectType>;
(run(o).a: { k: 'a', v: boolean });
(run(o).b: { k: 'b', v: string });
(run(o).a: { k: 'b', v: boolean });
(run(o).b: { k: 'b', v: number });
run(o).c;
|
Cannot cast `run(...).a` to object type because string literal `a` [1] is incompatible with string literal `b` [2] in property `k`. [incompatible-cast]
Cannot cast `run(...).b` to object type because string [1] is incompatible with number [2] in property `v`. [incompatible-cast]
Cannot get `run(...).c` because property `c` is missing in object type [1]. [prop-missing]
{"value":"// @flow\nconst o = {\n a: () => true,\n b: () => 'foo'\n};\n\ntype ExtractReturnObjectType = <K, V>(K, () => V) => { k: K, v: V };\n\ndeclare function run<O: Object>(o: O): $ObjMapi<O, ExtractReturnObjectType>;\n\n(run(o).a: { k: 'a', v: boolean }); // Ok\n(run(o).b: { k: 'b', v: string }); // Ok\n// $ExpectError\n(run(o).a: { k: 'b', v: boolean }); // Nope, a.k is \"a\"\n// $ExpectError\n(run(o).b: { k: 'b', v: number }); // Nope, b.v is a string\n// $ExpectError\nrun(o).c; // Nope, c was not in the original object\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_CONST","context":"normal","value":"const","line":2,"start":9,"end":14},{"type":"T_IDENTIFIER","context":"normal","value":"o","line":2,"start":15,"end":16},{"type":"T_ASSIGN","context":"normal","value":"=","line":2,"start":17,"end":18},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":19,"end":20},{"type":"T_IDENTIFIER","context":"normal","value":"a","line":3,"start":23,"end":24},{"type":"T_COLON","context":"normal","value":":","line":3,"start":24,"end":25},{"type":"T_LPAREN","context":"normal","value":"(","line":3,"start":26,"end":27},{"type":"T_RPAREN","context":"normal","value":")","line":3,"start":27,"end":28},{"type":"T_ARROW","context":"normal","value":"=>","line":3,"start":29,"end":31},{"type":"T_TRUE","context":"normal","value":"true","line":3,"start":32,"end":36},{"type":"T_COMMA","context":"normal","value":",","line":3,"start":36,"end":37},{"type":"T_IDENTIFIER","context":"normal","value":"b","line":4,"start":40,"end":41},{"type":"T_COLON","context":"normal","value":":","line":4,"start":41,"end":42},{"type":"T_LPAREN","context":"normal","value":"(","line":4,"start":43,"end":44},{"type":"T_RPAREN","context":"normal","value":")","line":4,"start":44,"end":45},{"type":"T_ARROW","context":"normal","value":"=>","line":4,"start":46,"end":48},{"type":"T_STRING","context":"normal","value":"'foo'","line":4,"start":49,"end":54},{"type":"T_RCURLY","context":"normal","value":"}","line":5,"start":55,"end":56},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":56,"end":57},{"type":"T_TYPE","context":"normal","value":"type","line":7,"start":59,"end":63},{"type":"T_IDENTIFIER","context":"type","value":"ExtractReturnObjectType","line":7,"start":64,"end":87},{"type":"T_ASSIGN","context":"type","value":"=","line":7,"start":88,"end":89},{"type":"T_LESS_THAN","context":"type","value":"<","line":7,"start":90,"end":91},{"type":"T_IDENTIFIER","context":"type","value":"K","line":7,"start":91,"end":92},{"type":"T_COMMA","context":"type","value":",","line":7,"start":92,"end":93},{"type":"T_IDENTIFIER","context":"type","value":"V","line":7,"start":94,"end":95},{"type":"T_GREATER_THAN","context":"type","value":">","line":7,"start":95,"end":96},{"type":"T_LPAREN","context":"type","value":"(","line":7,"start":96,"end":97},{"type":"T_IDENTIFIER","context":"type","value":"K","line":7,"start":97,"end":98},{"type":"T_COMMA","context":"type","value":",","line":7,"start":98,"end":99},{"type":"T_LPAREN","context":"type","value":"(","line":7,"start":100,"end":101},{"type":"T_RPAREN","context":"type","value":")","line":7,"start":101,"end":102},{"type":"T_ARROW","context":"type","value":"=>","line":7,"start":103,"end":105},{"type":"T_IDENTIFIER","context":"type","value":"V","line":7,"start":106,"end":107},{"type":"T_RPAREN","context":"type","value":")","line":7,"start":107,"end":108},{"type":"T_ARROW","context":"type","value":"=>","line":7,"start":109,"end":111},{"type":"T_LCURLY","context":"type","value":"{","line":7,"start":112,"end":113},{"type":"T_IDENTIFIER","context":"normal","value":"k","line":7,"start":114,"end":115},{"type":"T_COLON","context":"type","value":":","line":7,"start":115,"end":116},{"type":"T_IDENTIFIER","context":"type","value":"K","line":7,"start":117,"end":118},{"type":"T_COMMA","context":"type","value":",","line":7,"start":118,"end":119},{"type":"T_IDENTIFIER","context":"normal","value":"v","line":7,"start":120,"end":121},{"type":"T_COLON","context":"type","value":":","line":7,"start":121,"end":122},{"type":"T_IDENTIFIER","context":"type","value":"V","line":7,"start":123,"end":124},{"type":"T_RCURLY","context":"type","value":"}","line":7,"start":125,"end":126},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":126,"end":127},{"type":"T_DECLARE","context":"normal","value":"declare","line":9,"start":129,"end":136},{"type":"T_FUNCTION","context":"normal","value":"function","line":9,"start":137,"end":145},{"type":"T_IDENTIFIER","context":"normal","value":"run","line":9,"start":146,"end":149},{"type":"T_LESS_THAN","context":"type","value":"<","line":9,"start":149,"end":150},{"type":"T_IDENTIFIER","context":"type","value":"O","line":9,"start":150,"end":151},{"type":"T_COLON","context":"type","value":":","line":9,"start":151,"end":152},{"type":"T_IDENTIFIER","context":"type","value":"Object","line":9,"start":153,"end":159},{"type":"T_GREATER_THAN","context":"type","value":">","line":9,"start":159,"end":160},{"type":"T_LPAREN","context":"type","value":"(","line":9,"start":160,"end":161},{"type":"T_IDENTIFIER","context":"normal","value":"o","line":9,"start":161,"end":162},{"type":"T_COLON","context":"type","value":":","line":9,"start":162,"end":163},{"type":"T_IDENTIFIER","context":"type","value":"O","line":9,"start":164,"end":165},{"type":"T_RPAREN","context":"type","value":")","line":9,"start":165,"end":166},{"type":"T_COLON","context":"normal","value":":","line":9,"start":166,"end":167},{"type":"T_IDENTIFIER","context":"type","value":"$ObjMapi","line":9,"start":168,"end":176},{"type":"T_LESS_THAN","context":"type","value":"<","line":9,"start":176,"end":177},{"type":"T_IDENTIFIER","context":"type","value":"O","line":9,"start":177,"end":178},{"type":"T_COMMA","context":"type","value":",","line":9,"start":178,"end":179},{"type":"T_IDENTIFIER","context":"type","value":"ExtractReturnObjectType","line":9,"start":180,"end":203},{"type":"T_GREATER_THAN","context":"type","value":">","line":9,"start":203,"end":204},{"type":"T_SEMICOLON","context":"normal","value":";","line":9,"start":204,"end":205},{"type":"T_LPAREN","context":"normal","value":"(","line":11,"start":207,"end":208},{"type":"T_IDENTIFIER","context":"normal","value":"run","line":11,"start":208,"end":211},{"type":"T_LPAREN","context":"normal","value":"(","line":11,"start":211,"end":212},{"type":"T_IDENTIFIER","context":"normal","value":"o","line":11,"start":212,"end":213},{"type":"T_RPAREN","context":"normal","value":")","line":11,"start":213,"end":214},{"type":"T_PERIOD","context":"normal","value":".","line":11,"start":214,"end":215},{"type":"T_IDENTIFIER","context":"normal","value":"a","line":11,"start":215,"end":216},{"type":"T_COLON","context":"type","value":":","line":11,"start":216,"end":217},{"type":"T_LCURLY","context":"type","value":"{","line":11,"start":218,"end":219},{"type":"T_IDENTIFIER","context":"normal","value":"k","line":11,"start":220,"end":221},{"type":"T_COLON","context":"type","value":":","line":11,"start":221,"end":222},{"type":"T_STRING","context":"type","value":"'a'","line":11,"start":223,"end":226},{"type":"T_COMMA","context":"type","value":",","line":11,"start":226,"end":227},{"type":"T_IDENTIFIER","context":"normal","value":"v","line":11,"start":228,"end":229},{"type":"T_COLON","context":"type","value":":","line":11,"start":229,"end":230},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":11,"start":231,"end":238},{"type":"T_RCURLY","context":"type","value":"}","line":11,"start":239,"end":240},{"type":"T_RPAREN","context":"normal","value":")","line":11,"start":240,"end":241},{"type":"T_SEMICOLON","context":"normal","value":";","line":11,"start":241,"end":242},{"type":"Line","context":"comment","value":"// Ok","line":11,"start":243,"end":248},{"type":"T_LPAREN","context":"normal","value":"(","line":12,"start":249,"end":250},{"type":"T_IDENTIFIER","context":"normal","value":"run","line":12,"start":250,"end":253},{"type":"T_LPAREN","context":"normal","value":"(","line":12,"start":253,"end":254},{"type":"T_IDENTIFIER","context":"normal","value":"o","line":12,"start":254,"end":255},{"type":"T_RPAREN","context":"normal","value":")","line":12,"start":255,"end":256},{"type":"T_PERIOD","context":"normal","value":".","line":12,"start":256,"end":257},{"type":"T_IDENTIFIER","context":"normal","value":"b","line":12,"start":257,"end":258},{"type":"T_COLON","context":"type","value":":","line":12,"start":258,"end":259},{"type":"T_LCURLY","context":"type","value":"{","line":12,"start":260,"end":261},{"type":"T_IDENTIFIER","context":"normal","value":"k","line":12,"start":262,"end":263},{"type":"T_COLON","context":"type","value":":","line":12,"start":263,"end":264},{"type":"T_STRING","context":"type","value":"'b'","line":12,"start":265,"end":268},{"type":"T_COMMA","context":"type","value":",","line":12,"start":268,"end":269},{"type":"T_IDENTIFIER","context":"normal","value":"v","line":12,"start":270,"end":271},{"type":"T_COLON","context":"type","value":":","line":12,"start":271,"end":272},{"type":"T_STRING_TYPE","context":"type","value":"string","line":12,"start":273,"end":279},{"type":"T_RCURLY","context":"type","value":"}","line":12,"start":280,"end":281},{"type":"T_RPAREN","context":"normal","value":")","line":12,"start":281,"end":282},{"type":"T_SEMICOLON","context":"normal","value":";","line":12,"start":282,"end":283},{"type":"Line","context":"comment","value":"// Ok","line":12,"start":285,"end":290},{"type":"Line","context":"comment","value":"// $ExpectError","line":13,"start":291,"end":306},{"type":"T_LPAREN","context":"normal","value":"(","line":14,"start":307,"end":308},{"type":"T_IDENTIFIER","context":"normal","value":"run","line":14,"start":308,"end":311},{"type":"T_LPAREN","context":"normal","value":"(","line":14,"start":311,"end":312},{"type":"T_IDENTIFIER","context":"normal","value":"o","line":14,"start":312,"end":313},{"type":"T_RPAREN","context":"normal","value":")","line":14,"start":313,"end":314},{"type":"T_PERIOD","context":"normal","value":".","line":14,"start":314,"end":315},{"type":"T_IDENTIFIER","context":"normal","value":"a","line":14,"start":315,"end":316},{"type":"T_COLON","context":"type","value":":","line":14,"start":316,"end":317},{"type":"T_LCURLY","context":"type","value":"{","line":14,"start":318,"end":319},{"type":"T_IDENTIFIER","context":"normal","value":"k","line":14,"start":320,"end":321},{"type":"T_COLON","context":"type","value":":","line":14,"start":321,"end":322},{"type":"T_STRING","context":"type","value":"'b'","line":14,"start":323,"end":326},{"type":"T_COMMA","context":"type","value":",","line":14,"start":326,"end":327},{"type":"T_IDENTIFIER","context":"normal","value":"v","line":14,"start":328,"end":329},{"type":"T_COLON","context":"type","value":":","line":14,"start":329,"end":330},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":14,"start":331,"end":338},{"type":"T_RCURLY","context":"type","value":"}","line":14,"start":339,"end":340},{"type":"T_RPAREN","context":"normal","value":")","line":14,"start":340,"end":341},{"type":"T_SEMICOLON","context":"normal","value":";","line":14,"start":341,"end":342},{"type":"Line","context":"comment","value":"// Nope, a.k is \"a\"","line":14,"start":343,"end":362},{"type":"Line","context":"comment","value":"// $ExpectError","line":15,"start":363,"end":378},{"type":"T_LPAREN","context":"normal","value":"(","line":16,"start":379,"end":380},{"type":"T_IDENTIFIER","context":"normal","value":"run","line":16,"start":380,"end":383},{"type":"T_LPAREN","context":"normal","value":"(","line":16,"start":383,"end":384},{"type":"T_IDENTIFIER","context":"normal","value":"o","line":16,"start":384,"end":385},{"type":"T_RPAREN","context":"normal","value":")","line":16,"start":385,"end":386},{"type":"T_PERIOD","context":"normal","value":".","line":16,"start":386,"end":387},{"type":"T_IDENTIFIER","context":"normal","value":"b","line":16,"start":387,"end":388},{"type":"T_COLON","context":"type","value":":","line":16,"start":388,"end":389},{"type":"T_LCURLY","context":"type","value":"{","line":16,"start":390,"end":391},{"type":"T_IDENTIFIER","context":"normal","value":"k","line":16,"start":392,"end":393},{"type":"T_COLON","context":"type","value":":","line":16,"start":393,"end":394},{"type":"T_STRING","context":"type","value":"'b'","line":16,"start":395,"end":398},{"type":"T_COMMA","context":"type","value":",","line":16,"start":398,"end":399},{"type":"T_IDENTIFIER","context":"normal","value":"v","line":16,"start":400,"end":401},{"type":"T_COLON","context":"type","value":":","line":16,"start":401,"end":402},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":16,"start":403,"end":409},{"type":"T_RCURLY","context":"type","value":"}","line":16,"start":410,"end":411},{"type":"T_RPAREN","context":"normal","value":")","line":16,"start":411,"end":412},{"type":"T_SEMICOLON","context":"normal","value":";","line":16,"start":412,"end":413},{"type":"Line","context":"comment","value":"// Nope, b.v is a string","line":16,"start":415,"end":439},{"type":"Line","context":"comment","value":"// $ExpectError","line":17,"start":440,"end":455},{"type":"T_IDENTIFIER","context":"normal","value":"run","line":18,"start":456,"end":459},{"type":"T_LPAREN","context":"normal","value":"(","line":18,"start":459,"end":460},{"type":"T_IDENTIFIER","context":"normal","value":"o","line":18,"start":460,"end":461},{"type":"T_RPAREN","context":"normal","value":")","line":18,"start":461,"end":462},{"type":"T_PERIOD","context":"normal","value":".","line":18,"start":462,"end":463},{"type":"T_IDENTIFIER","context":"normal","value":"c","line":18,"start":463,"end":464},{"type":"T_SEMICOLON","context":"normal","value":";","line":18,"start":464,"end":465},{"type":"Line","context":"comment","value":"// Nope, c was not in the original object","line":18,"start":492,"end":533}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot cast `run(...).a` to object type because string literal `a` [1] is incompatible with string literal `b` [2] in property `k`. [incompatible-cast]","context":"(run(o).a: { k: 'b', v: boolean }); // Nope, a.k is \"a\"","source":"-","start":{"line":14,"column":2,"offset":308},"end":{"line":14,"column":9,"offset":316}}],"operation":null},{"id":"E2","messages":[{"id":"E2M1","description":"Cannot cast `run(...).b` to object type because string [1] is incompatible with number [2] in property `v`. [incompatible-cast]","context":"(run(o).b: { k: 'b', v: number }); // Nope, b.v is a string","source":"-","start":{"line":16,"column":2,"offset":380},"end":{"line":16,"column":9,"offset":388}}],"operation":null},{"id":"E3","messages":[{"id":"E3M1","description":"Cannot get `run(...).c` because property `c` is missing in object type [1]. [prop-missing]","context":"run(o).c; // Nope, c was not in the original object","source":"-","start":{"line":18,"column":8,"offset":463},"end":{"line":18,"column":8,"offset":464}}],"operation":null}]}
$TupleMap<T, F>
$TupleMap<T, F>
takes an iterable type T
(e.g.: Tuple
or Array
), and a function type F
, and returns the iterable type obtained by mapping the type of each value in the iterable with the provided function type F
. This is analogous to the Javascript function map
.
Following our example from $ObjMap<T>
, let’s assume that run
takes an array of functions, instead of an object, and maps over them returning an array of the function call results. We could annotate its return type like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
type ExtractReturnType = <V>(() => V) => V
function run<A, I: Array<() => A>>(iter: I): $TupleMap<I, ExtractReturnType> {
return iter.map(fn => fn());
}
const arr = [() => 'foo', () => 'bar'];
(run(arr)[0]: string);
(run(arr)[1]: string);
(run(arr)[1]: boolean);
|
Cannot cast `run(...)[1]` to boolean because string [1] is incompatible with boolean [2]. [incompatible-cast]
{"value":"// @flow\n\n// Function type that takes a `() => V` and returns a `V` (its return type)\ntype ExtractReturnType = <V>(() => V) => V\n\nfunction run<A, I: Array<() => A>>(iter: I): $TupleMap<I, ExtractReturnType> {\n return iter.map(fn => fn());\n}\n\nconst arr = [() => 'foo', () => 'bar'];\n(run(arr)[0]: string); // OK\n(run(arr)[1]: string); // OK\n(run(arr)[1]: boolean); // Error\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"Line","context":"comment","value":"// Function type that takes a `() => V` and returns a `V` (its return type)","line":3,"start":10,"end":85},{"type":"T_TYPE","context":"normal","value":"type","line":4,"start":86,"end":90},{"type":"T_IDENTIFIER","context":"type","value":"ExtractReturnType","line":4,"start":91,"end":108},{"type":"T_ASSIGN","context":"type","value":"=","line":4,"start":109,"end":110},{"type":"T_LESS_THAN","context":"type","value":"<","line":4,"start":111,"end":112},{"type":"T_IDENTIFIER","context":"type","value":"V","line":4,"start":112,"end":113},{"type":"T_GREATER_THAN","context":"type","value":">","line":4,"start":113,"end":114},{"type":"T_LPAREN","context":"type","value":"(","line":4,"start":114,"end":115},{"type":"T_LPAREN","context":"type","value":"(","line":4,"start":115,"end":116},{"type":"T_RPAREN","context":"type","value":")","line":4,"start":116,"end":117},{"type":"T_ARROW","context":"type","value":"=>","line":4,"start":118,"end":120},{"type":"T_IDENTIFIER","context":"type","value":"V","line":4,"start":121,"end":122},{"type":"T_RPAREN","context":"type","value":")","line":4,"start":122,"end":123},{"type":"T_ARROW","context":"type","value":"=>","line":4,"start":124,"end":126},{"type":"T_IDENTIFIER","context":"type","value":"V","line":4,"start":127,"end":128},{"type":"T_FUNCTION","context":"normal","value":"function","line":6,"start":130,"end":138},{"type":"T_IDENTIFIER","context":"normal","value":"run","line":6,"start":139,"end":142},{"type":"T_LESS_THAN","context":"type","value":"<","line":6,"start":142,"end":143},{"type":"T_IDENTIFIER","context":"type","value":"A","line":6,"start":143,"end":144},{"type":"T_COMMA","context":"type","value":",","line":6,"start":144,"end":145},{"type":"T_IDENTIFIER","context":"type","value":"I","line":6,"start":146,"end":147},{"type":"T_COLON","context":"type","value":":","line":6,"start":147,"end":148},{"type":"T_IDENTIFIER","context":"type","value":"Array","line":6,"start":149,"end":154},{"type":"T_LESS_THAN","context":"type","value":"<","line":6,"start":154,"end":155},{"type":"T_LPAREN","context":"type","value":"(","line":6,"start":155,"end":156},{"type":"T_RPAREN","context":"type","value":")","line":6,"start":156,"end":157},{"type":"T_ARROW","context":"type","value":"=>","line":6,"start":158,"end":160},{"type":"T_IDENTIFIER","context":"type","value":"A","line":6,"start":161,"end":162},{"type":"T_GREATER_THAN","context":"type","value":">","line":6,"start":162,"end":163},{"type":"T_GREATER_THAN","context":"type","value":">","line":6,"start":163,"end":164},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":164,"end":165},{"type":"T_IDENTIFIER","context":"normal","value":"iter","line":6,"start":165,"end":169},{"type":"T_COLON","context":"type","value":":","line":6,"start":169,"end":170},{"type":"T_IDENTIFIER","context":"type","value":"I","line":6,"start":171,"end":172},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":172,"end":173},{"type":"T_COLON","context":"type","value":":","line":6,"start":173,"end":174},{"type":"T_IDENTIFIER","context":"type","value":"$TupleMap","line":6,"start":175,"end":184},{"type":"T_LESS_THAN","context":"type","value":"<","line":6,"start":184,"end":185},{"type":"T_IDENTIFIER","context":"type","value":"I","line":6,"start":185,"end":186},{"type":"T_COMMA","context":"type","value":",","line":6,"start":186,"end":187},{"type":"T_IDENTIFIER","context":"type","value":"ExtractReturnType","line":6,"start":188,"end":205},{"type":"T_GREATER_THAN","context":"type","value":">","line":6,"start":205,"end":206},{"type":"T_LCURLY","context":"normal","value":"{","line":6,"start":207,"end":208},{"type":"T_RETURN","context":"normal","value":"return","line":7,"start":211,"end":217},{"type":"T_IDENTIFIER","context":"normal","value":"iter","line":7,"start":218,"end":222},{"type":"T_PERIOD","context":"normal","value":".","line":7,"start":222,"end":223},{"type":"T_IDENTIFIER","context":"normal","value":"map","line":7,"start":223,"end":226},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":226,"end":227},{"type":"T_IDENTIFIER","context":"normal","value":"fn","line":7,"start":227,"end":229},{"type":"T_ARROW","context":"normal","value":"=>","line":7,"start":230,"end":232},{"type":"T_IDENTIFIER","context":"normal","value":"fn","line":7,"start":233,"end":235},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":235,"end":236},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":236,"end":237},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":237,"end":238},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":238,"end":239},{"type":"T_RCURLY","context":"normal","value":"}","line":8,"start":240,"end":241},{"type":"T_CONST","context":"normal","value":"const","line":10,"start":243,"end":248},{"type":"T_IDENTIFIER","context":"normal","value":"arr","line":10,"start":249,"end":252},{"type":"T_ASSIGN","context":"normal","value":"=","line":10,"start":253,"end":254},{"type":"T_LBRACKET","context":"normal","value":"[","line":10,"start":255,"end":256},{"type":"T_LPAREN","context":"normal","value":"(","line":10,"start":256,"end":257},{"type":"T_RPAREN","context":"normal","value":")","line":10,"start":257,"end":258},{"type":"T_ARROW","context":"normal","value":"=>","line":10,"start":259,"end":261},{"type":"T_STRING","context":"normal","value":"'foo'","line":10,"start":262,"end":267},{"type":"T_COMMA","context":"normal","value":",","line":10,"start":267,"end":268},{"type":"T_LPAREN","context":"normal","value":"(","line":10,"start":269,"end":270},{"type":"T_RPAREN","context":"normal","value":")","line":10,"start":270,"end":271},{"type":"T_ARROW","context":"normal","value":"=>","line":10,"start":272,"end":274},{"type":"T_STRING","context":"normal","value":"'bar'","line":10,"start":275,"end":280},{"type":"T_RBRACKET","context":"normal","value":"]","line":10,"start":280,"end":281},{"type":"T_SEMICOLON","context":"normal","value":";","line":10,"start":281,"end":282},{"type":"T_LPAREN","context":"normal","value":"(","line":11,"start":283,"end":284},{"type":"T_IDENTIFIER","context":"normal","value":"run","line":11,"start":284,"end":287},{"type":"T_LPAREN","context":"normal","value":"(","line":11,"start":287,"end":288},{"type":"T_IDENTIFIER","context":"normal","value":"arr","line":11,"start":288,"end":291},{"type":"T_RPAREN","context":"normal","value":")","line":11,"start":291,"end":292},{"type":"T_LBRACKET","context":"normal","value":"[","line":11,"start":292,"end":293},{"type":"T_NUMBER","context":"normal","value":"0","line":11,"start":293,"end":294},{"type":"T_RBRACKET","context":"normal","value":"]","line":11,"start":294,"end":295},{"type":"T_COLON","context":"type","value":":","line":11,"start":295,"end":296},{"type":"T_STRING_TYPE","context":"type","value":"string","line":11,"start":297,"end":303},{"type":"T_RPAREN","context":"normal","value":")","line":11,"start":303,"end":304},{"type":"T_SEMICOLON","context":"normal","value":";","line":11,"start":304,"end":305},{"type":"Line","context":"comment","value":"// OK","line":11,"start":306,"end":311},{"type":"T_LPAREN","context":"normal","value":"(","line":12,"start":312,"end":313},{"type":"T_IDENTIFIER","context":"normal","value":"run","line":12,"start":313,"end":316},{"type":"T_LPAREN","context":"normal","value":"(","line":12,"start":316,"end":317},{"type":"T_IDENTIFIER","context":"normal","value":"arr","line":12,"start":317,"end":320},{"type":"T_RPAREN","context":"normal","value":")","line":12,"start":320,"end":321},{"type":"T_LBRACKET","context":"normal","value":"[","line":12,"start":321,"end":322},{"type":"T_NUMBER","context":"normal","value":"1","line":12,"start":322,"end":323},{"type":"T_RBRACKET","context":"normal","value":"]","line":12,"start":323,"end":324},{"type":"T_COLON","context":"type","value":":","line":12,"start":324,"end":325},{"type":"T_STRING_TYPE","context":"type","value":"string","line":12,"start":326,"end":332},{"type":"T_RPAREN","context":"normal","value":")","line":12,"start":332,"end":333},{"type":"T_SEMICOLON","context":"normal","value":";","line":12,"start":333,"end":334},{"type":"Line","context":"comment","value":"// OK","line":12,"start":335,"end":340},{"type":"T_LPAREN","context":"normal","value":"(","line":13,"start":341,"end":342},{"type":"T_IDENTIFIER","context":"normal","value":"run","line":13,"start":342,"end":345},{"type":"T_LPAREN","context":"normal","value":"(","line":13,"start":345,"end":346},{"type":"T_IDENTIFIER","context":"normal","value":"arr","line":13,"start":346,"end":349},{"type":"T_RPAREN","context":"normal","value":")","line":13,"start":349,"end":350},{"type":"T_LBRACKET","context":"normal","value":"[","line":13,"start":350,"end":351},{"type":"T_NUMBER","context":"normal","value":"1","line":13,"start":351,"end":352},{"type":"T_RBRACKET","context":"normal","value":"]","line":13,"start":352,"end":353},{"type":"T_COLON","context":"type","value":":","line":13,"start":353,"end":354},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":13,"start":355,"end":362},{"type":"T_RPAREN","context":"normal","value":")","line":13,"start":362,"end":363},{"type":"T_SEMICOLON","context":"normal","value":";","line":13,"start":363,"end":364},{"type":"Line","context":"comment","value":"// Error","line":13,"start":365,"end":373}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot cast `run(...)[1]` to boolean because string [1] is incompatible with boolean [2]. [incompatible-cast]","context":"(run(arr)[1]: boolean); // Error","source":"-","start":{"line":13,"column":2,"offset":342},"end":{"line":13,"column":12,"offset":353}}],"operation":null}]}
$Call<F, T...>
$Call<F, T...>
is a type that represents the result of calling the given function type F
with 0 or more arguments T...
. This is analogous to calling a function at runtime (or more specifically, it’s analogous to calling Function.prototype.call
), but at the type level; this means that function type calls happens statically, i.e. not at runtime.
Let’s see a couple of examples:
1
2
3
4
5
6
7
8
9
10
11
|
type ExtractPropType = <T>({prop: T}) => T;
type Obj = {prop: number};
type PropType = $Call<ExtractPropType, Obj>;
type Nope = $Call<ExtractPropType, {nope: number}>;
(5: PropType);
(true: PropType);
(5: Nope);
|
Cannot instantiate `$Call` because property `prop` is missing in object type [1] but exists in object type [2] in the first argument. [prop-missing]
Cannot cast `true` to `PropType` because boolean [1] is incompatible with number [2]. [incompatible-cast]
{"value":"// @flow\n\n// Takes an object type, returns the type of its `prop` key\ntype ExtractPropType = <T>({prop: T}) => T;\ntype Obj = {prop: number};\ntype PropType = $Call<ExtractPropType, Obj>; // Call `ExtractPropType` with `Obj` as an argument\ntype Nope = $Call<ExtractPropType, {nope: number}>; // Error: argument doesn't match `Obj`.\n\n(5: PropType); // OK\n(true: PropType); // Error: PropType is a number\n(5: Nope); // Error\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"Line","context":"comment","value":"// Takes an object type, returns the type of its `prop` key","line":3,"start":10,"end":69},{"type":"T_TYPE","context":"normal","value":"type","line":4,"start":70,"end":74},{"type":"T_IDENTIFIER","context":"type","value":"ExtractPropType","line":4,"start":75,"end":90},{"type":"T_ASSIGN","context":"type","value":"=","line":4,"start":91,"end":92},{"type":"T_LESS_THAN","context":"type","value":"<","line":4,"start":93,"end":94},{"type":"T_IDENTIFIER","context":"type","value":"T","line":4,"start":94,"end":95},{"type":"T_GREATER_THAN","context":"type","value":">","line":4,"start":95,"end":96},{"type":"T_LPAREN","context":"type","value":"(","line":4,"start":96,"end":97},{"type":"T_LCURLY","context":"type","value":"{","line":4,"start":97,"end":98},{"type":"T_IDENTIFIER","context":"normal","value":"prop","line":4,"start":98,"end":102},{"type":"T_COLON","context":"type","value":":","line":4,"start":102,"end":103},{"type":"T_IDENTIFIER","context":"type","value":"T","line":4,"start":104,"end":105},{"type":"T_RCURLY","context":"type","value":"}","line":4,"start":105,"end":106},{"type":"T_RPAREN","context":"type","value":")","line":4,"start":106,"end":107},{"type":"T_ARROW","context":"type","value":"=>","line":4,"start":108,"end":110},{"type":"T_IDENTIFIER","context":"type","value":"T","line":4,"start":111,"end":112},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":112,"end":113},{"type":"T_TYPE","context":"normal","value":"type","line":5,"start":114,"end":118},{"type":"T_IDENTIFIER","context":"type","value":"Obj","line":5,"start":119,"end":122},{"type":"T_ASSIGN","context":"type","value":"=","line":5,"start":123,"end":124},{"type":"T_LCURLY","context":"type","value":"{","line":5,"start":125,"end":126},{"type":"T_IDENTIFIER","context":"normal","value":"prop","line":5,"start":126,"end":130},{"type":"T_COLON","context":"type","value":":","line":5,"start":130,"end":131},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":5,"start":132,"end":138},{"type":"T_RCURLY","context":"type","value":"}","line":5,"start":138,"end":139},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":139,"end":140},{"type":"T_TYPE","context":"normal","value":"type","line":6,"start":141,"end":145},{"type":"T_IDENTIFIER","context":"type","value":"PropType","line":6,"start":146,"end":154},{"type":"T_ASSIGN","context":"type","value":"=","line":6,"start":155,"end":156},{"type":"T_IDENTIFIER","context":"type","value":"$Call","line":6,"start":157,"end":162},{"type":"T_LESS_THAN","context":"type","value":"<","line":6,"start":162,"end":163},{"type":"T_IDENTIFIER","context":"type","value":"ExtractPropType","line":6,"start":163,"end":178},{"type":"T_COMMA","context":"type","value":",","line":6,"start":178,"end":179},{"type":"T_IDENTIFIER","context":"type","value":"Obj","line":6,"start":180,"end":183},{"type":"T_GREATER_THAN","context":"type","value":">","line":6,"start":183,"end":184},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":184,"end":185},{"type":"Line","context":"comment","value":"// Call `ExtractPropType` with `Obj` as an argument","line":6,"start":187,"end":238},{"type":"T_TYPE","context":"normal","value":"type","line":7,"start":239,"end":243},{"type":"T_IDENTIFIER","context":"type","value":"Nope","line":7,"start":244,"end":248},{"type":"T_ASSIGN","context":"type","value":"=","line":7,"start":249,"end":250},{"type":"T_IDENTIFIER","context":"type","value":"$Call","line":7,"start":251,"end":256},{"type":"T_LESS_THAN","context":"type","value":"<","line":7,"start":256,"end":257},{"type":"T_IDENTIFIER","context":"type","value":"ExtractPropType","line":7,"start":257,"end":272},{"type":"T_COMMA","context":"type","value":",","line":7,"start":272,"end":273},{"type":"T_LCURLY","context":"type","value":"{","line":7,"start":274,"end":275},{"type":"T_IDENTIFIER","context":"normal","value":"nope","line":7,"start":275,"end":279},{"type":"T_COLON","context":"type","value":":","line":7,"start":279,"end":280},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":7,"start":281,"end":287},{"type":"T_RCURLY","context":"type","value":"}","line":7,"start":287,"end":288},{"type":"T_GREATER_THAN","context":"type","value":">","line":7,"start":288,"end":289},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":289,"end":290},{"type":"Line","context":"comment","value":"// Error: argument doesn't match `Obj`.","line":7,"start":292,"end":331},{"type":"T_LPAREN","context":"normal","value":"(","line":9,"start":333,"end":334},{"type":"T_NUMBER","context":"normal","value":"5","line":9,"start":334,"end":335},{"type":"T_COLON","context":"type","value":":","line":9,"start":335,"end":336},{"type":"T_IDENTIFIER","context":"type","value":"PropType","line":9,"start":337,"end":345},{"type":"T_RPAREN","context":"normal","value":")","line":9,"start":345,"end":346},{"type":"T_SEMICOLON","context":"normal","value":";","line":9,"start":346,"end":347},{"type":"Line","context":"comment","value":"// OK","line":9,"start":348,"end":353},{"type":"T_LPAREN","context":"normal","value":"(","line":10,"start":354,"end":355},{"type":"T_TRUE","context":"normal","value":"true","line":10,"start":355,"end":359},{"type":"T_COLON","context":"type","value":":","line":10,"start":359,"end":360},{"type":"T_IDENTIFIER","context":"type","value":"PropType","line":10,"start":361,"end":369},{"type":"T_RPAREN","context":"normal","value":")","line":10,"start":369,"end":370},{"type":"T_SEMICOLON","context":"normal","value":";","line":10,"start":370,"end":371},{"type":"Line","context":"comment","value":"// Error: PropType is a number","line":10,"start":373,"end":403},{"type":"T_LPAREN","context":"normal","value":"(","line":11,"start":404,"end":405},{"type":"T_NUMBER","context":"normal","value":"5","line":11,"start":405,"end":406},{"type":"T_COLON","context":"type","value":":","line":11,"start":406,"end":407},{"type":"T_IDENTIFIER","context":"type","value":"Nope","line":11,"start":408,"end":412},{"type":"T_RPAREN","context":"normal","value":")","line":11,"start":412,"end":413},{"type":"T_SEMICOLON","context":"normal","value":";","line":11,"start":413,"end":414},{"type":"Line","context":"comment","value":"// Error","line":11,"start":416,"end":424}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot instantiate `$Call` because property `prop` is missing in object type [1] but exists in object type [2] in the first argument. [prop-missing]","context":"type Nope = $Call<ExtractPropType, {nope: number}>; // Error: argument doesn't match `Obj`.","source":"-","start":{"line":7,"column":36,"offset":274},"end":{"line":7,"column":49,"offset":288}}],"operation":null},{"id":"E2","messages":[{"id":"E2M1","description":"Cannot cast `true` to `PropType` because boolean [1] is incompatible with number [2]. [incompatible-cast]","context":"(true: PropType); // Error: PropType is a number","source":"-","start":{"line":10,"column":2,"offset":355},"end":{"line":10,"column":5,"offset":359}}],"operation":null}]}
1
2
3
4
5
6
7
8
9
10
|
type ExtractReturnType = <R>(() => R) => R;
type Fn = () => number;
type ReturnType = $Call<ExtractReturnType, Fn>
(5: ReturnType);
(true: ReturnType);
|
Cannot cast `true` to `ReturnType` because boolean [1] is incompatible with number [2]. [incompatible-cast]
{"value":"// @flow\n\n// Takes a function type, and returns its return type\n// This is useful if you want to get the return type of some function without actually calling it at runtime.\ntype ExtractReturnType = <R>(() => R) => R;\ntype Fn = () => number;\ntype ReturnType = $Call<ExtractReturnType, Fn> // Call `ExtractReturnType` with `Fn` as an argument\n\n(5: ReturnType); // OK\n(true: ReturnType); // Error: ReturnType is a number\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"Line","context":"comment","value":"// Takes a function type, and returns its return type","line":3,"start":10,"end":63},{"type":"Line","context":"comment","value":"// This is useful if you want to get the return type of some function without actually calling it at runtime.","line":4,"start":64,"end":173},{"type":"T_TYPE","context":"normal","value":"type","line":5,"start":174,"end":178},{"type":"T_IDENTIFIER","context":"type","value":"ExtractReturnType","line":5,"start":179,"end":196},{"type":"T_ASSIGN","context":"type","value":"=","line":5,"start":197,"end":198},{"type":"T_LESS_THAN","context":"type","value":"<","line":5,"start":199,"end":200},{"type":"T_IDENTIFIER","context":"type","value":"R","line":5,"start":200,"end":201},{"type":"T_GREATER_THAN","context":"type","value":">","line":5,"start":201,"end":202},{"type":"T_LPAREN","context":"type","value":"(","line":5,"start":202,"end":203},{"type":"T_LPAREN","context":"type","value":"(","line":5,"start":203,"end":204},{"type":"T_RPAREN","context":"type","value":")","line":5,"start":204,"end":205},{"type":"T_ARROW","context":"type","value":"=>","line":5,"start":206,"end":208},{"type":"T_IDENTIFIER","context":"type","value":"R","line":5,"start":209,"end":210},{"type":"T_RPAREN","context":"type","value":")","line":5,"start":210,"end":211},{"type":"T_ARROW","context":"type","value":"=>","line":5,"start":212,"end":214},{"type":"T_IDENTIFIER","context":"type","value":"R","line":5,"start":215,"end":216},{"type":"T_SEMICOLON","context":"normal","value":";","line":5,"start":216,"end":217},{"type":"T_TYPE","context":"normal","value":"type","line":6,"start":218,"end":222},{"type":"T_IDENTIFIER","context":"type","value":"Fn","line":6,"start":223,"end":225},{"type":"T_ASSIGN","context":"type","value":"=","line":6,"start":226,"end":227},{"type":"T_LPAREN","context":"type","value":"(","line":6,"start":228,"end":229},{"type":"T_RPAREN","context":"type","value":")","line":6,"start":229,"end":230},{"type":"T_ARROW","context":"type","value":"=>","line":6,"start":231,"end":233},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":6,"start":234,"end":240},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":240,"end":241},{"type":"T_TYPE","context":"normal","value":"type","line":7,"start":242,"end":246},{"type":"T_IDENTIFIER","context":"type","value":"ReturnType","line":7,"start":247,"end":257},{"type":"T_ASSIGN","context":"type","value":"=","line":7,"start":258,"end":259},{"type":"T_IDENTIFIER","context":"type","value":"$Call","line":7,"start":260,"end":265},{"type":"T_LESS_THAN","context":"type","value":"<","line":7,"start":265,"end":266},{"type":"T_IDENTIFIER","context":"type","value":"ExtractReturnType","line":7,"start":266,"end":283},{"type":"T_COMMA","context":"type","value":",","line":7,"start":283,"end":284},{"type":"T_IDENTIFIER","context":"type","value":"Fn","line":7,"start":285,"end":287},{"type":"T_GREATER_THAN","context":"type","value":">","line":7,"start":287,"end":288},{"type":"Line","context":"comment","value":"// Call `ExtractReturnType` with `Fn` as an argument","line":7,"start":289,"end":341},{"type":"T_LPAREN","context":"normal","value":"(","line":9,"start":343,"end":344},{"type":"T_NUMBER","context":"normal","value":"5","line":9,"start":344,"end":345},{"type":"T_COLON","context":"type","value":":","line":9,"start":345,"end":346},{"type":"T_IDENTIFIER","context":"type","value":"ReturnType","line":9,"start":347,"end":357},{"type":"T_RPAREN","context":"normal","value":")","line":9,"start":357,"end":358},{"type":"T_SEMICOLON","context":"normal","value":";","line":9,"start":358,"end":359},{"type":"Line","context":"comment","value":"// OK","line":9,"start":361,"end":366},{"type":"T_LPAREN","context":"normal","value":"(","line":10,"start":367,"end":368},{"type":"T_TRUE","context":"normal","value":"true","line":10,"start":368,"end":372},{"type":"T_COLON","context":"type","value":":","line":10,"start":372,"end":373},{"type":"T_IDENTIFIER","context":"type","value":"ReturnType","line":10,"start":374,"end":384},{"type":"T_RPAREN","context":"normal","value":")","line":10,"start":384,"end":385},{"type":"T_SEMICOLON","context":"normal","value":";","line":10,"start":385,"end":386},{"type":"Line","context":"comment","value":"// Error: ReturnType is a number","line":10,"start":388,"end":420}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot cast `true` to `ReturnType` because boolean [1] is incompatible with number [2]. [incompatible-cast]","context":"(true: ReturnType); // Error: ReturnType is a number","source":"-","start":{"line":10,"column":2,"offset":368},"end":{"line":10,"column":5,"offset":372}}],"operation":null}]}
$Call
can be very powerful because it allows you to make calls in type-land that you would otherwise have to do at runtime. The type-land calls happen statically and will be erased at runtime.
Let’s look at a couple of more advanced examples:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
type NestedObj = {|
+status: ?number,
+data: ?$ReadOnlyArray<{|
+foo: ?{|
+bar: number,
|},
|}>,
|};
type BarType = $Call<
<T>({
+data: ?$ReadOnlyArray<{
+foo: ?{
+bar: ?T
},
}>,
}) => T,
NestedObj,
>;
(5: BarType);
(true: BarType);
|
Cannot cast `true` to `BarType` because boolean [1] is incompatible with number [2]. [incompatible-cast]
{"value":"// @flow\n\n// Extracting deeply nested types:\ntype NestedObj = {|\n +status: ?number,\n +data: ?$ReadOnlyArray<{|\n +foo: ?{|\n +bar: number,\n |},\n |}>,\n|};\n\n// If you wanted to extract the type for `bar`, you could use $Call:\ntype BarType = $Call<\n <T>({\n +data: ?$ReadOnlyArray<{\n +foo: ?{\n +bar: ?T\n },\n }>,\n }) => T,\n NestedObj,\n>;\n\n(5: BarType);\n(true: BarType); // Error: `bar` is not a boolean\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"Line","context":"comment","value":"// Extracting deeply nested types:","line":3,"start":10,"end":44},{"type":"T_TYPE","context":"normal","value":"type","line":4,"start":45,"end":49},{"type":"T_IDENTIFIER","context":"type","value":"NestedObj","line":4,"start":50,"end":59},{"type":"T_ASSIGN","context":"type","value":"=","line":4,"start":60,"end":61},{"type":"T_LCURLYBAR","context":"type","value":"{|","line":4,"start":62,"end":64},{"type":"T_PLUS","context":"type","value":"+","line":5,"start":67,"end":68},{"type":"T_IDENTIFIER","context":"normal","value":"status","line":5,"start":68,"end":74},{"type":"T_COLON","context":"type","value":":","line":5,"start":74,"end":75},{"type":"T_PLING","context":"type","value":"?","line":5,"start":76,"end":77},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":5,"start":77,"end":83},{"type":"T_COMMA","context":"type","value":",","line":5,"start":83,"end":84},{"type":"T_PLUS","context":"type","value":"+","line":6,"start":87,"end":88},{"type":"T_IDENTIFIER","context":"normal","value":"data","line":6,"start":88,"end":92},{"type":"T_COLON","context":"type","value":":","line":6,"start":92,"end":93},{"type":"T_PLING","context":"type","value":"?","line":6,"start":94,"end":95},{"type":"T_IDENTIFIER","context":"type","value":"$ReadOnlyArray","line":6,"start":95,"end":109},{"type":"T_LESS_THAN","context":"type","value":"<","line":6,"start":109,"end":110},{"type":"T_LCURLYBAR","context":"type","value":"{|","line":6,"start":110,"end":112},{"type":"T_PLUS","context":"type","value":"+","line":7,"start":117,"end":118},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":7,"start":118,"end":121},{"type":"T_COLON","context":"type","value":":","line":7,"start":121,"end":122},{"type":"T_PLING","context":"type","value":"?","line":7,"start":123,"end":124},{"type":"T_LCURLYBAR","context":"type","value":"{|","line":7,"start":124,"end":126},{"type":"T_PLUS","context":"type","value":"+","line":8,"start":134,"end":135},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":8,"start":135,"end":138},{"type":"T_COLON","context":"type","value":":","line":8,"start":138,"end":139},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":8,"start":140,"end":146},{"type":"T_COMMA","context":"type","value":",","line":8,"start":146,"end":147},{"type":"T_RCURLYBAR","context":"type","value":"|}","line":9,"start":152,"end":154},{"type":"T_COMMA","context":"type","value":",","line":9,"start":154,"end":155},{"type":"T_RCURLYBAR","context":"type","value":"|}","line":10,"start":158,"end":160},{"type":"T_GREATER_THAN","context":"type","value":">","line":10,"start":160,"end":161},{"type":"T_COMMA","context":"type","value":",","line":10,"start":161,"end":162},{"type":"T_RCURLYBAR","context":"type","value":"|}","line":11,"start":163,"end":165},{"type":"T_SEMICOLON","context":"normal","value":";","line":11,"start":165,"end":166},{"type":"Line","context":"comment","value":"// If you wanted to extract the type for `bar`, you could use $Call:","line":13,"start":168,"end":236},{"type":"T_TYPE","context":"normal","value":"type","line":14,"start":237,"end":241},{"type":"T_IDENTIFIER","context":"type","value":"BarType","line":14,"start":242,"end":249},{"type":"T_ASSIGN","context":"type","value":"=","line":14,"start":250,"end":251},{"type":"T_IDENTIFIER","context":"type","value":"$Call","line":14,"start":252,"end":257},{"type":"T_LESS_THAN","context":"type","value":"<","line":14,"start":257,"end":258},{"type":"T_LESS_THAN","context":"type","value":"<","line":15,"start":261,"end":262},{"type":"T_IDENTIFIER","context":"type","value":"T","line":15,"start":262,"end":263},{"type":"T_GREATER_THAN","context":"type","value":">","line":15,"start":263,"end":264},{"type":"T_LPAREN","context":"type","value":"(","line":15,"start":264,"end":265},{"type":"T_LCURLY","context":"type","value":"{","line":15,"start":265,"end":266},{"type":"T_PLUS","context":"type","value":"+","line":16,"start":271,"end":272},{"type":"T_IDENTIFIER","context":"normal","value":"data","line":16,"start":272,"end":276},{"type":"T_COLON","context":"type","value":":","line":16,"start":276,"end":277},{"type":"T_PLING","context":"type","value":"?","line":16,"start":278,"end":279},{"type":"T_IDENTIFIER","context":"type","value":"$ReadOnlyArray","line":16,"start":279,"end":293},{"type":"T_LESS_THAN","context":"type","value":"<","line":16,"start":293,"end":294},{"type":"T_LCURLY","context":"type","value":"{","line":16,"start":294,"end":295},{"type":"T_PLUS","context":"type","value":"+","line":17,"start":302,"end":303},{"type":"T_IDENTIFIER","context":"normal","value":"foo","line":17,"start":303,"end":306},{"type":"T_COLON","context":"type","value":":","line":17,"start":306,"end":307},{"type":"T_PLING","context":"type","value":"?","line":17,"start":308,"end":309},{"type":"T_LCURLY","context":"type","value":"{","line":17,"start":309,"end":310},{"type":"T_PLUS","context":"type","value":"+","line":18,"start":319,"end":320},{"type":"T_IDENTIFIER","context":"normal","value":"bar","line":18,"start":320,"end":323},{"type":"T_COLON","context":"type","value":":","line":18,"start":323,"end":324},{"type":"T_PLING","context":"type","value":"?","line":18,"start":325,"end":326},{"type":"T_IDENTIFIER","context":"type","value":"T","line":18,"start":326,"end":327},{"type":"T_RCURLY","context":"type","value":"}","line":19,"start":334,"end":335},{"type":"T_COMMA","context":"type","value":",","line":19,"start":335,"end":336},{"type":"T_RCURLY","context":"type","value":"}","line":20,"start":341,"end":342},{"type":"T_GREATER_THAN","context":"type","value":">","line":20,"start":342,"end":343},{"type":"T_COMMA","context":"type","value":",","line":20,"start":343,"end":344},{"type":"T_RCURLY","context":"type","value":"}","line":21,"start":347,"end":348},{"type":"T_RPAREN","context":"type","value":")","line":21,"start":348,"end":349},{"type":"T_ARROW","context":"type","value":"=>","line":21,"start":350,"end":352},{"type":"T_IDENTIFIER","context":"type","value":"T","line":21,"start":353,"end":354},{"type":"T_COMMA","context":"type","value":",","line":21,"start":354,"end":355},{"type":"T_IDENTIFIER","context":"type","value":"NestedObj","line":22,"start":358,"end":367},{"type":"T_COMMA","context":"type","value":",","line":22,"start":367,"end":368},{"type":"T_GREATER_THAN","context":"type","value":">","line":23,"start":369,"end":370},{"type":"T_SEMICOLON","context":"normal","value":";","line":23,"start":370,"end":371},{"type":"T_LPAREN","context":"normal","value":"(","line":25,"start":373,"end":374},{"type":"T_NUMBER","context":"normal","value":"5","line":25,"start":374,"end":375},{"type":"T_COLON","context":"type","value":":","line":25,"start":375,"end":376},{"type":"T_IDENTIFIER","context":"type","value":"BarType","line":25,"start":377,"end":384},{"type":"T_RPAREN","context":"normal","value":")","line":25,"start":384,"end":385},{"type":"T_SEMICOLON","context":"normal","value":";","line":25,"start":385,"end":386},{"type":"T_LPAREN","context":"normal","value":"(","line":26,"start":387,"end":388},{"type":"T_TRUE","context":"normal","value":"true","line":26,"start":388,"end":392},{"type":"T_COLON","context":"type","value":":","line":26,"start":392,"end":393},{"type":"T_IDENTIFIER","context":"type","value":"BarType","line":26,"start":394,"end":401},{"type":"T_RPAREN","context":"normal","value":")","line":26,"start":401,"end":402},{"type":"T_SEMICOLON","context":"normal","value":";","line":26,"start":402,"end":403},{"type":"Line","context":"comment","value":"// Error: `bar` is not a boolean","line":26,"start":405,"end":437}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot cast `true` to `BarType` because boolean [1] is incompatible with number [2]. [incompatible-cast]","context":"(true: BarType); // Error: `bar` is not a boolean","source":"-","start":{"line":26,"column":2,"offset":388},"end":{"line":26,"column":5,"offset":392}}],"operation":null}]}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
function getFirstValue<V>(map: Map<string, V>): ?V {
for (const [key, value] of map.entries()) {
return value;
}
return null;
}
type Value = $Call<typeof getFirstValue, Map<string, number>>;
(5: Value);
(true: Value);
type GetMapValue<M> =
$Call<typeof getFirstValue, M>;
(5: GetMapValue<Map<string, number>>);
(true: GetMapValue<Map<string, boolean>>);
(true: GetMapValue<Map<string, number>>);
|
Cannot cast `true` to `Value` because boolean [1] is incompatible with number [2]. [incompatible-cast]
Cannot cast `true` to `GetMapValue` because boolean [1] is incompatible with number [2]. [incompatible-cast]
{"value":"// @flow\n\n// Getting return types:\nfunction getFirstValue<V>(map: Map<string, V>): ?V {\n for (const [key, value] of map.entries()) {\n return value;\n }\n return null;\n}\n\n// Using $Call, we can get the actual return type of the function above, without calling it at runtime:\ntype Value = $Call<typeof getFirstValue, Map<string, number>>;\n\n(5: Value);\n(true: Value); // Error: Value is a `number`\n\n\n// We could generalize it further:\ntype GetMapValue<M> =\n $Call<typeof getFirstValue, M>;\n\n(5: GetMapValue<Map<string, number>>);\n(true: GetMapValue<Map<string, boolean>>);\n(true: GetMapValue<Map<string, number>>); // Error: value is a `number`\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"Line","context":"comment","value":"// Getting return types:","line":3,"start":10,"end":34},{"type":"T_FUNCTION","context":"normal","value":"function","line":4,"start":35,"end":43},{"type":"T_IDENTIFIER","context":"normal","value":"getFirstValue","line":4,"start":44,"end":57},{"type":"T_LESS_THAN","context":"type","value":"<","line":4,"start":57,"end":58},{"type":"T_IDENTIFIER","context":"type","value":"V","line":4,"start":58,"end":59},{"type":"T_GREATER_THAN","context":"type","value":">","line":4,"start":59,"end":60},{"type":"T_LPAREN","context":"normal","value":"(","line":4,"start":60,"end":61},{"type":"T_IDENTIFIER","context":"normal","value":"map","line":4,"start":61,"end":64},{"type":"T_COLON","context":"type","value":":","line":4,"start":64,"end":65},{"type":"T_IDENTIFIER","context":"type","value":"Map","line":4,"start":66,"end":69},{"type":"T_LESS_THAN","context":"type","value":"<","line":4,"start":69,"end":70},{"type":"T_STRING_TYPE","context":"type","value":"string","line":4,"start":70,"end":76},{"type":"T_COMMA","context":"type","value":",","line":4,"start":76,"end":77},{"type":"T_IDENTIFIER","context":"type","value":"V","line":4,"start":78,"end":79},{"type":"T_GREATER_THAN","context":"type","value":">","line":4,"start":79,"end":80},{"type":"T_RPAREN","context":"normal","value":")","line":4,"start":80,"end":81},{"type":"T_COLON","context":"type","value":":","line":4,"start":81,"end":82},{"type":"T_PLING","context":"type","value":"?","line":4,"start":83,"end":84},{"type":"T_IDENTIFIER","context":"type","value":"V","line":4,"start":84,"end":85},{"type":"T_LCURLY","context":"normal","value":"{","line":4,"start":86,"end":87},{"type":"T_FOR","context":"normal","value":"for","line":5,"start":90,"end":93},{"type":"T_LPAREN","context":"normal","value":"(","line":5,"start":94,"end":95},{"type":"T_CONST","context":"normal","value":"const","line":5,"start":95,"end":100},{"type":"T_LBRACKET","context":"normal","value":"[","line":5,"start":101,"end":102},{"type":"T_IDENTIFIER","context":"normal","value":"key","line":5,"start":102,"end":105},{"type":"T_COMMA","context":"normal","value":",","line":5,"start":105,"end":106},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":5,"start":107,"end":112},{"type":"T_RBRACKET","context":"normal","value":"]","line":5,"start":112,"end":113},{"type":"T_OF","context":"normal","value":"of","line":5,"start":114,"end":116},{"type":"T_IDENTIFIER","context":"normal","value":"map","line":5,"start":117,"end":120},{"type":"T_PERIOD","context":"normal","value":".","line":5,"start":120,"end":121},{"type":"T_IDENTIFIER","context":"normal","value":"entries","line":5,"start":121,"end":128},{"type":"T_LPAREN","context":"normal","value":"(","line":5,"start":128,"end":129},{"type":"T_RPAREN","context":"normal","value":")","line":5,"start":129,"end":130},{"type":"T_RPAREN","context":"normal","value":")","line":5,"start":130,"end":131},{"type":"T_LCURLY","context":"normal","value":"{","line":5,"start":132,"end":133},{"type":"T_RETURN","context":"normal","value":"return","line":6,"start":138,"end":144},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":6,"start":145,"end":150},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":150,"end":151},{"type":"T_RCURLY","context":"normal","value":"}","line":7,"start":154,"end":155},{"type":"T_RETURN","context":"normal","value":"return","line":8,"start":158,"end":164},{"type":"T_NULL","context":"normal","value":"null","line":8,"start":165,"end":169},{"type":"T_SEMICOLON","context":"normal","value":";","line":8,"start":169,"end":170},{"type":"T_RCURLY","context":"normal","value":"}","line":9,"start":171,"end":172},{"type":"Line","context":"comment","value":"// Using $Call, we can get the actual return type of the function above, without calling it at runtime:","line":11,"start":174,"end":277},{"type":"T_TYPE","context":"normal","value":"type","line":12,"start":278,"end":282},{"type":"T_IDENTIFIER","context":"type","value":"Value","line":12,"start":283,"end":288},{"type":"T_ASSIGN","context":"type","value":"=","line":12,"start":289,"end":290},{"type":"T_IDENTIFIER","context":"type","value":"$Call","line":12,"start":291,"end":296},{"type":"T_LESS_THAN","context":"type","value":"<","line":12,"start":296,"end":297},{"type":"T_TYPEOF","context":"type","value":"typeof","line":12,"start":297,"end":303},{"type":"T_IDENTIFIER","context":"type","value":"getFirstValue","line":12,"start":304,"end":317},{"type":"T_COMMA","context":"type","value":",","line":12,"start":317,"end":318},{"type":"T_IDENTIFIER","context":"type","value":"Map","line":12,"start":319,"end":322},{"type":"T_LESS_THAN","context":"type","value":"<","line":12,"start":322,"end":323},{"type":"T_STRING_TYPE","context":"type","value":"string","line":12,"start":323,"end":329},{"type":"T_COMMA","context":"type","value":",","line":12,"start":329,"end":330},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":12,"start":331,"end":337},{"type":"T_GREATER_THAN","context":"type","value":">","line":12,"start":337,"end":338},{"type":"T_GREATER_THAN","context":"type","value":">","line":12,"start":338,"end":339},{"type":"T_SEMICOLON","context":"normal","value":";","line":12,"start":339,"end":340},{"type":"T_LPAREN","context":"normal","value":"(","line":14,"start":342,"end":343},{"type":"T_NUMBER","context":"normal","value":"5","line":14,"start":343,"end":344},{"type":"T_COLON","context":"type","value":":","line":14,"start":344,"end":345},{"type":"T_IDENTIFIER","context":"type","value":"Value","line":14,"start":346,"end":351},{"type":"T_RPAREN","context":"normal","value":")","line":14,"start":351,"end":352},{"type":"T_SEMICOLON","context":"normal","value":";","line":14,"start":352,"end":353},{"type":"T_LPAREN","context":"normal","value":"(","line":15,"start":354,"end":355},{"type":"T_TRUE","context":"normal","value":"true","line":15,"start":355,"end":359},{"type":"T_COLON","context":"type","value":":","line":15,"start":359,"end":360},{"type":"T_IDENTIFIER","context":"type","value":"Value","line":15,"start":361,"end":366},{"type":"T_RPAREN","context":"normal","value":")","line":15,"start":366,"end":367},{"type":"T_SEMICOLON","context":"normal","value":";","line":15,"start":367,"end":368},{"type":"Line","context":"comment","value":"// Error: Value is a `number`","line":15,"start":370,"end":399},{"type":"Line","context":"comment","value":"// We could generalize it further:","line":18,"start":402,"end":436},{"type":"T_TYPE","context":"normal","value":"type","line":19,"start":437,"end":441},{"type":"T_IDENTIFIER","context":"type","value":"GetMapValue","line":19,"start":442,"end":453},{"type":"T_LESS_THAN","context":"type","value":"<","line":19,"start":453,"end":454},{"type":"T_IDENTIFIER","context":"type","value":"M","line":19,"start":454,"end":455},{"type":"T_GREATER_THAN","context":"type","value":">","line":19,"start":455,"end":456},{"type":"T_ASSIGN","context":"type","value":"=","line":19,"start":457,"end":458},{"type":"T_IDENTIFIER","context":"type","value":"$Call","line":20,"start":461,"end":466},{"type":"T_LESS_THAN","context":"type","value":"<","line":20,"start":466,"end":467},{"type":"T_TYPEOF","context":"type","value":"typeof","line":20,"start":467,"end":473},{"type":"T_IDENTIFIER","context":"type","value":"getFirstValue","line":20,"start":474,"end":487},{"type":"T_COMMA","context":"type","value":",","line":20,"start":487,"end":488},{"type":"T_IDENTIFIER","context":"type","value":"M","line":20,"start":489,"end":490},{"type":"T_GREATER_THAN","context":"type","value":">","line":20,"start":490,"end":491},{"type":"T_SEMICOLON","context":"normal","value":";","line":20,"start":491,"end":492},{"type":"T_LPAREN","context":"normal","value":"(","line":22,"start":494,"end":495},{"type":"T_NUMBER","context":"normal","value":"5","line":22,"start":495,"end":496},{"type":"T_COLON","context":"type","value":":","line":22,"start":496,"end":497},{"type":"T_IDENTIFIER","context":"type","value":"GetMapValue","line":22,"start":498,"end":509},{"type":"T_LESS_THAN","context":"type","value":"<","line":22,"start":509,"end":510},{"type":"T_IDENTIFIER","context":"type","value":"Map","line":22,"start":510,"end":513},{"type":"T_LESS_THAN","context":"type","value":"<","line":22,"start":513,"end":514},{"type":"T_STRING_TYPE","context":"type","value":"string","line":22,"start":514,"end":520},{"type":"T_COMMA","context":"type","value":",","line":22,"start":520,"end":521},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":22,"start":522,"end":528},{"type":"T_GREATER_THAN","context":"type","value":">","line":22,"start":528,"end":529},{"type":"T_GREATER_THAN","context":"type","value":">","line":22,"start":529,"end":530},{"type":"T_RPAREN","context":"normal","value":")","line":22,"start":530,"end":531},{"type":"T_SEMICOLON","context":"normal","value":";","line":22,"start":531,"end":532},{"type":"T_LPAREN","context":"normal","value":"(","line":23,"start":533,"end":534},{"type":"T_TRUE","context":"normal","value":"true","line":23,"start":534,"end":538},{"type":"T_COLON","context":"type","value":":","line":23,"start":538,"end":539},{"type":"T_IDENTIFIER","context":"type","value":"GetMapValue","line":23,"start":540,"end":551},{"type":"T_LESS_THAN","context":"type","value":"<","line":23,"start":551,"end":552},{"type":"T_IDENTIFIER","context":"type","value":"Map","line":23,"start":552,"end":555},{"type":"T_LESS_THAN","context":"type","value":"<","line":23,"start":555,"end":556},{"type":"T_STRING_TYPE","context":"type","value":"string","line":23,"start":556,"end":562},{"type":"T_COMMA","context":"type","value":",","line":23,"start":562,"end":563},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":23,"start":564,"end":571},{"type":"T_GREATER_THAN","context":"type","value":">","line":23,"start":571,"end":572},{"type":"T_GREATER_THAN","context":"type","value":">","line":23,"start":572,"end":573},{"type":"T_RPAREN","context":"normal","value":")","line":23,"start":573,"end":574},{"type":"T_SEMICOLON","context":"normal","value":";","line":23,"start":574,"end":575},{"type":"T_LPAREN","context":"normal","value":"(","line":24,"start":576,"end":577},{"type":"T_TRUE","context":"normal","value":"true","line":24,"start":577,"end":581},{"type":"T_COLON","context":"type","value":":","line":24,"start":581,"end":582},{"type":"T_IDENTIFIER","context":"type","value":"GetMapValue","line":24,"start":583,"end":594},{"type":"T_LESS_THAN","context":"type","value":"<","line":24,"start":594,"end":595},{"type":"T_IDENTIFIER","context":"type","value":"Map","line":24,"start":595,"end":598},{"type":"T_LESS_THAN","context":"type","value":"<","line":24,"start":598,"end":599},{"type":"T_STRING_TYPE","context":"type","value":"string","line":24,"start":599,"end":605},{"type":"T_COMMA","context":"type","value":",","line":24,"start":605,"end":606},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":24,"start":607,"end":613},{"type":"T_GREATER_THAN","context":"type","value":">","line":24,"start":613,"end":614},{"type":"T_GREATER_THAN","context":"type","value":">","line":24,"start":614,"end":615},{"type":"T_RPAREN","context":"normal","value":")","line":24,"start":615,"end":616},{"type":"T_SEMICOLON","context":"normal","value":";","line":24,"start":616,"end":617},{"type":"Line","context":"comment","value":"// Error: value is a `number`","line":24,"start":619,"end":648}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot cast `true` to `Value` because boolean [1] is incompatible with number [2]. [incompatible-cast]","context":"(true: Value); // Error: Value is a `number`","source":"-","start":{"line":15,"column":2,"offset":355},"end":{"line":15,"column":5,"offset":359}}],"operation":null},{"id":"E2","messages":[{"id":"E2M1","description":"Cannot cast `true` to `GetMapValue` because boolean [1] is incompatible with number [2]. [incompatible-cast]","context":"(true: GetMapValue<Map<string, number>>); // Error: value is a `number`","source":"-","start":{"line":24,"column":2,"offset":577},"end":{"line":24,"column":5,"offset":581}}],"operation":null}]}
Class<T>
Given a type T
representing instances of a class C
, the type Class<T>
is the type of the class C
.
For example:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class Store {}
class ExtendedStore extends Store {}
class Model {}
function makeStore(storeClass: Class<Store>) {
return new storeClass();
}
(makeStore(Store): Store);
(makeStore(ExtendedStore): Store);
(makeStore(Model): Model);
(makeStore(ExtendedStore): Model);
|
Cannot cast `makeStore(...)` to `Model` because `Store` [1] is incompatible with `Model` [2]. [incompatible-cast]
Cannot call `makeStore` with `Model` bound to `storeClass` because `Model` [1] is incompatible with `Store` [2]. [incompatible-call]
Cannot cast `makeStore(...)` to `Model` because `Store` [1] is incompatible with `Model` [2]. [incompatible-cast]
{"value":"// @flow\nclass Store {}\nclass ExtendedStore extends Store {}\nclass Model {}\n\nfunction makeStore(storeClass: Class<Store>) {\n return new storeClass();\n}\n\n(makeStore(Store): Store);\n(makeStore(ExtendedStore): Store);\n(makeStore(Model): Model); // error\n(makeStore(ExtendedStore): Model); // Flow infers the return type\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_CLASS","context":"normal","value":"class","line":2,"start":9,"end":14},{"type":"T_IDENTIFIER","context":"normal","value":"Store","line":2,"start":15,"end":20},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":21,"end":22},{"type":"T_RCURLY","context":"normal","value":"}","line":2,"start":22,"end":23},{"type":"T_CLASS","context":"normal","value":"class","line":3,"start":24,"end":29},{"type":"T_IDENTIFIER","context":"normal","value":"ExtendedStore","line":3,"start":30,"end":43},{"type":"T_EXTENDS","context":"normal","value":"extends","line":3,"start":44,"end":51},{"type":"T_IDENTIFIER","context":"normal","value":"Store","line":3,"start":52,"end":57},{"type":"T_LCURLY","context":"normal","value":"{","line":3,"start":58,"end":59},{"type":"T_RCURLY","context":"normal","value":"}","line":3,"start":59,"end":60},{"type":"T_CLASS","context":"normal","value":"class","line":4,"start":61,"end":66},{"type":"T_IDENTIFIER","context":"normal","value":"Model","line":4,"start":67,"end":72},{"type":"T_LCURLY","context":"normal","value":"{","line":4,"start":73,"end":74},{"type":"T_RCURLY","context":"normal","value":"}","line":4,"start":74,"end":75},{"type":"T_FUNCTION","context":"normal","value":"function","line":6,"start":77,"end":85},{"type":"T_IDENTIFIER","context":"normal","value":"makeStore","line":6,"start":86,"end":95},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":95,"end":96},{"type":"T_IDENTIFIER","context":"normal","value":"storeClass","line":6,"start":96,"end":106},{"type":"T_COLON","context":"type","value":":","line":6,"start":106,"end":107},{"type":"T_IDENTIFIER","context":"type","value":"Class","line":6,"start":108,"end":113},{"type":"T_LESS_THAN","context":"type","value":"<","line":6,"start":113,"end":114},{"type":"T_IDENTIFIER","context":"type","value":"Store","line":6,"start":114,"end":119},{"type":"T_GREATER_THAN","context":"type","value":">","line":6,"start":119,"end":120},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":120,"end":121},{"type":"T_LCURLY","context":"normal","value":"{","line":6,"start":122,"end":123},{"type":"T_RETURN","context":"normal","value":"return","line":7,"start":126,"end":132},{"type":"T_NEW","context":"normal","value":"new","line":7,"start":133,"end":136},{"type":"T_IDENTIFIER","context":"normal","value":"storeClass","line":7,"start":137,"end":147},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":147,"end":148},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":148,"end":149},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":149,"end":150},{"type":"T_RCURLY","context":"normal","value":"}","line":8,"start":151,"end":152},{"type":"T_LPAREN","context":"normal","value":"(","line":10,"start":154,"end":155},{"type":"T_IDENTIFIER","context":"normal","value":"makeStore","line":10,"start":155,"end":164},{"type":"T_LPAREN","context":"normal","value":"(","line":10,"start":164,"end":165},{"type":"T_IDENTIFIER","context":"normal","value":"Store","line":10,"start":165,"end":170},{"type":"T_RPAREN","context":"normal","value":")","line":10,"start":170,"end":171},{"type":"T_COLON","context":"type","value":":","line":10,"start":171,"end":172},{"type":"T_IDENTIFIER","context":"type","value":"Store","line":10,"start":173,"end":178},{"type":"T_RPAREN","context":"normal","value":")","line":10,"start":178,"end":179},{"type":"T_SEMICOLON","context":"normal","value":";","line":10,"start":179,"end":180},{"type":"T_LPAREN","context":"normal","value":"(","line":11,"start":181,"end":182},{"type":"T_IDENTIFIER","context":"normal","value":"makeStore","line":11,"start":182,"end":191},{"type":"T_LPAREN","context":"normal","value":"(","line":11,"start":191,"end":192},{"type":"T_IDENTIFIER","context":"normal","value":"ExtendedStore","line":11,"start":192,"end":205},{"type":"T_RPAREN","context":"normal","value":")","line":11,"start":205,"end":206},{"type":"T_COLON","context":"type","value":":","line":11,"start":206,"end":207},{"type":"T_IDENTIFIER","context":"type","value":"Store","line":11,"start":208,"end":213},{"type":"T_RPAREN","context":"normal","value":")","line":11,"start":213,"end":214},{"type":"T_SEMICOLON","context":"normal","value":";","line":11,"start":214,"end":215},{"type":"T_LPAREN","context":"normal","value":"(","line":12,"start":216,"end":217},{"type":"T_IDENTIFIER","context":"normal","value":"makeStore","line":12,"start":217,"end":226},{"type":"T_LPAREN","context":"normal","value":"(","line":12,"start":226,"end":227},{"type":"T_IDENTIFIER","context":"normal","value":"Model","line":12,"start":227,"end":232},{"type":"T_RPAREN","context":"normal","value":")","line":12,"start":232,"end":233},{"type":"T_COLON","context":"type","value":":","line":12,"start":233,"end":234},{"type":"T_IDENTIFIER","context":"type","value":"Model","line":12,"start":235,"end":240},{"type":"T_RPAREN","context":"normal","value":")","line":12,"start":240,"end":241},{"type":"T_SEMICOLON","context":"normal","value":";","line":12,"start":241,"end":242},{"type":"Line","context":"comment","value":"// error","line":12,"start":243,"end":251},{"type":"T_LPAREN","context":"normal","value":"(","line":13,"start":252,"end":253},{"type":"T_IDENTIFIER","context":"normal","value":"makeStore","line":13,"start":253,"end":262},{"type":"T_LPAREN","context":"normal","value":"(","line":13,"start":262,"end":263},{"type":"T_IDENTIFIER","context":"normal","value":"ExtendedStore","line":13,"start":263,"end":276},{"type":"T_RPAREN","context":"normal","value":")","line":13,"start":276,"end":277},{"type":"T_COLON","context":"type","value":":","line":13,"start":277,"end":278},{"type":"T_IDENTIFIER","context":"type","value":"Model","line":13,"start":279,"end":284},{"type":"T_RPAREN","context":"normal","value":")","line":13,"start":284,"end":285},{"type":"T_SEMICOLON","context":"normal","value":";","line":13,"start":285,"end":286},{"type":"Line","context":"comment","value":"// Flow infers the return type","line":13,"start":287,"end":317}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot cast `makeStore(...)` to `Model` because `Store` [1] is incompatible with `Model` [2]. [incompatible-cast]","context":"(makeStore(Model): Model); // error","source":"-","start":{"line":12,"column":2,"offset":217},"end":{"line":12,"column":17,"offset":233}}],"operation":null},{"id":"E2","messages":[{"id":"E2M1","description":"Cannot call `makeStore` with `Model` bound to `storeClass` because `Model` [1] is incompatible with `Store` [2]. [incompatible-call]","context":"(makeStore(Model): Model); // error","source":"-","start":{"line":12,"column":12,"offset":227},"end":{"line":12,"column":16,"offset":232}}],"operation":null},{"id":"E3","messages":[{"id":"E3M1","description":"Cannot cast `makeStore(...)` to `Model` because `Store` [1] is incompatible with `Model` [2]. [incompatible-cast]","context":"(makeStore(ExtendedStore): Model); // Flow infers the return type","source":"-","start":{"line":13,"column":2,"offset":253},"end":{"line":13,"column":25,"offset":277}}],"operation":null}]}
For classes that take type parameters, you must also provide the parameter. For example:
1
2
3
4
5
6
7
8
9
10
|
class ParamStore<T> {
constructor(data: T) {}
}
function makeParamStore<T>(storeClass: Class<ParamStore<T>>, data: T): ParamStore<T> {
return new storeClass(data);
}
(makeParamStore(ParamStore, 1): ParamStore<number>);
(makeParamStore(ParamStore, 1): ParamStore<boolean>);
|
Cannot cast `makeParamStore(...)` to `ParamStore` because number [1] is incompatible with boolean [2] in type argument `T` [3]. [incompatible-cast]
{"value":"// @flow\nclass ParamStore<T> {\n constructor(data: T) {}\n}\n\nfunction makeParamStore<T>(storeClass: Class<ParamStore<T>>, data: T): ParamStore<T> {\n return new storeClass(data);\n}\n(makeParamStore(ParamStore, 1): ParamStore<number>);\n(makeParamStore(ParamStore, 1): ParamStore<boolean>); // failed because of the second parameter\n","tokens":[{"type":"Line","context":"comment","value":"// @flow","line":1,"start":0,"end":8},{"type":"T_CLASS","context":"normal","value":"class","line":2,"start":9,"end":14},{"type":"T_IDENTIFIER","context":"normal","value":"ParamStore","line":2,"start":15,"end":25},{"type":"T_LESS_THAN","context":"type","value":"<","line":2,"start":25,"end":26},{"type":"T_IDENTIFIER","context":"type","value":"T","line":2,"start":26,"end":27},{"type":"T_GREATER_THAN","context":"type","value":">","line":2,"start":27,"end":28},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":29,"end":30},{"type":"T_IDENTIFIER","context":"normal","value":"constructor","line":3,"start":33,"end":44},{"type":"T_LPAREN","context":"normal","value":"(","line":3,"start":44,"end":45},{"type":"T_IDENTIFIER","context":"normal","value":"data","line":3,"start":45,"end":49},{"type":"T_COLON","context":"type","value":":","line":3,"start":49,"end":50},{"type":"T_IDENTIFIER","context":"type","value":"T","line":3,"start":51,"end":52},{"type":"T_RPAREN","context":"normal","value":")","line":3,"start":52,"end":53},{"type":"T_LCURLY","context":"normal","value":"{","line":3,"start":54,"end":55},{"type":"T_RCURLY","context":"normal","value":"}","line":3,"start":55,"end":56},{"type":"T_RCURLY","context":"normal","value":"}","line":4,"start":57,"end":58},{"type":"T_FUNCTION","context":"normal","value":"function","line":6,"start":60,"end":68},{"type":"T_IDENTIFIER","context":"normal","value":"makeParamStore","line":6,"start":69,"end":83},{"type":"T_LESS_THAN","context":"type","value":"<","line":6,"start":83,"end":84},{"type":"T_IDENTIFIER","context":"type","value":"T","line":6,"start":84,"end":85},{"type":"T_GREATER_THAN","context":"type","value":">","line":6,"start":85,"end":86},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":86,"end":87},{"type":"T_IDENTIFIER","context":"normal","value":"storeClass","line":6,"start":87,"end":97},{"type":"T_COLON","context":"type","value":":","line":6,"start":97,"end":98},{"type":"T_IDENTIFIER","context":"type","value":"Class","line":6,"start":99,"end":104},{"type":"T_LESS_THAN","context":"type","value":"<","line":6,"start":104,"end":105},{"type":"T_IDENTIFIER","context":"type","value":"ParamStore","line":6,"start":105,"end":115},{"type":"T_LESS_THAN","context":"type","value":"<","line":6,"start":115,"end":116},{"type":"T_IDENTIFIER","context":"type","value":"T","line":6,"start":116,"end":117},{"type":"T_GREATER_THAN","context":"type","value":">","line":6,"start":117,"end":118},{"type":"T_GREATER_THAN","context":"type","value":">","line":6,"start":118,"end":119},{"type":"T_COMMA","context":"normal","value":",","line":6,"start":119,"end":120},{"type":"T_IDENTIFIER","context":"normal","value":"data","line":6,"start":121,"end":125},{"type":"T_COLON","context":"type","value":":","line":6,"start":125,"end":126},{"type":"T_IDENTIFIER","context":"type","value":"T","line":6,"start":127,"end":128},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":128,"end":129},{"type":"T_COLON","context":"type","value":":","line":6,"start":129,"end":130},{"type":"T_IDENTIFIER","context":"type","value":"ParamStore","line":6,"start":131,"end":141},{"type":"T_LESS_THAN","context":"type","value":"<","line":6,"start":141,"end":142},{"type":"T_IDENTIFIER","context":"type","value":"T","line":6,"start":142,"end":143},{"type":"T_GREATER_THAN","context":"type","value":">","line":6,"start":143,"end":144},{"type":"T_LCURLY","context":"normal","value":"{","line":6,"start":145,"end":146},{"type":"T_RETURN","context":"normal","value":"return","line":7,"start":149,"end":155},{"type":"T_NEW","context":"normal","value":"new","line":7,"start":156,"end":159},{"type":"T_IDENTIFIER","context":"normal","value":"storeClass","line":7,"start":160,"end":170},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":170,"end":171},{"type":"T_IDENTIFIER","context":"normal","value":"data","line":7,"start":171,"end":175},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":175,"end":176},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":176,"end":177},{"type":"T_RCURLY","context":"normal","value":"}","line":8,"start":178,"end":179},{"type":"T_LPAREN","context":"normal","value":"(","line":9,"start":180,"end":181},{"type":"T_IDENTIFIER","context":"normal","value":"makeParamStore","line":9,"start":181,"end":195},{"type":"T_LPAREN","context":"normal","value":"(","line":9,"start":195,"end":196},{"type":"T_IDENTIFIER","context":"normal","value":"ParamStore","line":9,"start":196,"end":206},{"type":"T_COMMA","context":"normal","value":",","line":9,"start":206,"end":207},{"type":"T_NUMBER","context":"normal","value":"1","line":9,"start":208,"end":209},{"type":"T_RPAREN","context":"normal","value":")","line":9,"start":209,"end":210},{"type":"T_COLON","context":"type","value":":","line":9,"start":210,"end":211},{"type":"T_IDENTIFIER","context":"type","value":"ParamStore","line":9,"start":212,"end":222},{"type":"T_LESS_THAN","context":"type","value":"<","line":9,"start":222,"end":223},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":9,"start":223,"end":229},{"type":"T_GREATER_THAN","context":"type","value":">","line":9,"start":229,"end":230},{"type":"T_RPAREN","context":"normal","value":")","line":9,"start":230,"end":231},{"type":"T_SEMICOLON","context":"normal","value":";","line":9,"start":231,"end":232},{"type":"T_LPAREN","context":"normal","value":"(","line":10,"start":233,"end":234},{"type":"T_IDENTIFIER","context":"normal","value":"makeParamStore","line":10,"start":234,"end":248},{"type":"T_LPAREN","context":"normal","value":"(","line":10,"start":248,"end":249},{"type":"T_IDENTIFIER","context":"normal","value":"ParamStore","line":10,"start":249,"end":259},{"type":"T_COMMA","context":"normal","value":",","line":10,"start":259,"end":260},{"type":"T_NUMBER","context":"normal","value":"1","line":10,"start":261,"end":262},{"type":"T_RPAREN","context":"normal","value":")","line":10,"start":262,"end":263},{"type":"T_COLON","context":"type","value":":","line":10,"start":263,"end":264},{"type":"T_IDENTIFIER","context":"type","value":"ParamStore","line":10,"start":265,"end":275},{"type":"T_LESS_THAN","context":"type","value":"<","line":10,"start":275,"end":276},{"type":"T_BOOLEAN_TYPE","context":"type","value":"boolean","line":10,"start":276,"end":283},{"type":"T_GREATER_THAN","context":"type","value":">","line":10,"start":283,"end":284},{"type":"T_RPAREN","context":"normal","value":")","line":10,"start":284,"end":285},{"type":"T_SEMICOLON","context":"normal","value":";","line":10,"start":285,"end":286},{"type":"Line","context":"comment","value":"// failed because of the second parameter","line":10,"start":287,"end":328}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot cast `makeParamStore(...)` to `ParamStore` because number [1] is incompatible with boolean [2] in type argument `T` [3]. [incompatible-cast]","context":"(makeParamStore(ParamStore, 1): ParamStore<boolean>); // failed because of the second parameter","source":"-","start":{"line":10,"column":29,"offset":261},"end":{"line":10,"column":29,"offset":262}}],"operation":null}]}
$Shape<T>
A variable of type $Shape<T>
, where T
is some object type, can be assigned objects o
that contain a subset of the properties included in T
. For each property p: S
of T
,
the type of a potential binding of p
in o
must be compatible with S
.
For example
1
2
3
4
5
6
7
8
9
10
11
12
13
|
type Person = {
age: number,
name: string,
}
type PersonDetails = $Shape<Person>;
const person1: Person = {age: 28};
const person2: Person = {name: 'a'};
const person3: PersonDetails = {age: 28};
const person4: PersonDetails = {name: 'a'};
const person5: PersonDetails = {age: 28, name: 'a'};
const person6: PersonDetails = {age: '28'};
|
Cannot assign object literal to `person1` because property `name` is missing in object literal [1] but exists in `Person` [2]. [prop-missing]
Cannot assign object literal to `person2` because property `age` is missing in object literal [1] but exists in `Person` [2]. [prop-missing]
Cannot assign object literal to `person6` because string [1] is incompatible with number [2] in property `age`. [incompatible-type]
{"value":"// @flow\ntype Person = {\n age: number,\n name: string,\n}\ntype PersonDetails = $Shape<Person>;\n\nconst person1: Person = {age: 28}; // Error: missing `name`\nconst person2: Person = {name: 'a'}; // Error: missing `age`\nconst person3: PersonDetails = {age: 28}; // OK\nconst person4: PersonDetails = {name: 'a'}; // OK\nconst person5: PersonDetails = {age: 28, name: 'a'}; // OK\nconst person6: PersonDetails = {age: '28'}; // Error: string is incompatible with number\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":"Person","line":2,"start":14,"end":20},{"type":"T_ASSIGN","context":"type","value":"=","line":2,"start":21,"end":22},{"type":"T_LCURLY","context":"type","value":"{","line":2,"start":23,"end":24},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":3,"start":27,"end":30},{"type":"T_COLON","context":"type","value":":","line":3,"start":30,"end":31},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":3,"start":32,"end":38},{"type":"T_COMMA","context":"type","value":",","line":3,"start":38,"end":39},{"type":"T_IDENTIFIER","context":"normal","value":"name","line":4,"start":42,"end":46},{"type":"T_COLON","context":"type","value":":","line":4,"start":46,"end":47},{"type":"T_STRING_TYPE","context":"type","value":"string","line":4,"start":48,"end":54},{"type":"T_COMMA","context":"type","value":",","line":4,"start":54,"end":55},{"type":"T_RCURLY","context":"type","value":"}","line":5,"start":56,"end":57},{"type":"T_TYPE","context":"normal","value":"type","line":6,"start":58,"end":62},{"type":"T_IDENTIFIER","context":"type","value":"PersonDetails","line":6,"start":63,"end":76},{"type":"T_ASSIGN","context":"type","value":"=","line":6,"start":77,"end":78},{"type":"T_IDENTIFIER","context":"type","value":"$Shape","line":6,"start":79,"end":85},{"type":"T_LESS_THAN","context":"type","value":"<","line":6,"start":85,"end":86},{"type":"T_IDENTIFIER","context":"type","value":"Person","line":6,"start":86,"end":92},{"type":"T_GREATER_THAN","context":"type","value":">","line":6,"start":92,"end":93},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":93,"end":94},{"type":"T_CONST","context":"normal","value":"const","line":8,"start":96,"end":101},{"type":"T_IDENTIFIER","context":"normal","value":"person1","line":8,"start":102,"end":109},{"type":"T_COLON","context":"type","value":":","line":8,"start":109,"end":110},{"type":"T_IDENTIFIER","context":"type","value":"Person","line":8,"start":111,"end":117},{"type":"T_ASSIGN","context":"normal","value":"=","line":8,"start":118,"end":119},{"type":"T_LCURLY","context":"normal","value":"{","line":8,"start":120,"end":121},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":8,"start":121,"end":124},{"type":"T_COLON","context":"normal","value":":","line":8,"start":124,"end":125},{"type":"T_NUMBER","context":"normal","value":"28","line":8,"start":126,"end":128},{"type":"T_RCURLY","context":"normal","value":"}","line":8,"start":128,"end":129},{"type":"T_SEMICOLON","context":"normal","value":";","line":8,"start":129,"end":130},{"type":"Line","context":"comment","value":"// Error: missing `name`","line":8,"start":132,"end":156},{"type":"T_CONST","context":"normal","value":"const","line":9,"start":157,"end":162},{"type":"T_IDENTIFIER","context":"normal","value":"person2","line":9,"start":163,"end":170},{"type":"T_COLON","context":"type","value":":","line":9,"start":170,"end":171},{"type":"T_IDENTIFIER","context":"type","value":"Person","line":9,"start":172,"end":178},{"type":"T_ASSIGN","context":"normal","value":"=","line":9,"start":179,"end":180},{"type":"T_LCURLY","context":"normal","value":"{","line":9,"start":181,"end":182},{"type":"T_IDENTIFIER","context":"normal","value":"name","line":9,"start":182,"end":186},{"type":"T_COLON","context":"normal","value":":","line":9,"start":186,"end":187},{"type":"T_STRING","context":"normal","value":"'a'","line":9,"start":188,"end":191},{"type":"T_RCURLY","context":"normal","value":"}","line":9,"start":191,"end":192},{"type":"T_SEMICOLON","context":"normal","value":";","line":9,"start":192,"end":193},{"type":"Line","context":"comment","value":"// Error: missing `age`","line":9,"start":195,"end":218},{"type":"T_CONST","context":"normal","value":"const","line":10,"start":219,"end":224},{"type":"T_IDENTIFIER","context":"normal","value":"person3","line":10,"start":225,"end":232},{"type":"T_COLON","context":"type","value":":","line":10,"start":232,"end":233},{"type":"T_IDENTIFIER","context":"type","value":"PersonDetails","line":10,"start":234,"end":247},{"type":"T_ASSIGN","context":"normal","value":"=","line":10,"start":248,"end":249},{"type":"T_LCURLY","context":"normal","value":"{","line":10,"start":250,"end":251},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":10,"start":251,"end":254},{"type":"T_COLON","context":"normal","value":":","line":10,"start":254,"end":255},{"type":"T_NUMBER","context":"normal","value":"28","line":10,"start":256,"end":258},{"type":"T_RCURLY","context":"normal","value":"}","line":10,"start":258,"end":259},{"type":"T_SEMICOLON","context":"normal","value":";","line":10,"start":259,"end":260},{"type":"Line","context":"comment","value":"// OK","line":10,"start":262,"end":267},{"type":"T_CONST","context":"normal","value":"const","line":11,"start":268,"end":273},{"type":"T_IDENTIFIER","context":"normal","value":"person4","line":11,"start":274,"end":281},{"type":"T_COLON","context":"type","value":":","line":11,"start":281,"end":282},{"type":"T_IDENTIFIER","context":"type","value":"PersonDetails","line":11,"start":283,"end":296},{"type":"T_ASSIGN","context":"normal","value":"=","line":11,"start":297,"end":298},{"type":"T_LCURLY","context":"normal","value":"{","line":11,"start":299,"end":300},{"type":"T_IDENTIFIER","context":"normal","value":"name","line":11,"start":300,"end":304},{"type":"T_COLON","context":"normal","value":":","line":11,"start":304,"end":305},{"type":"T_STRING","context":"normal","value":"'a'","line":11,"start":306,"end":309},{"type":"T_RCURLY","context":"normal","value":"}","line":11,"start":309,"end":310},{"type":"T_SEMICOLON","context":"normal","value":";","line":11,"start":310,"end":311},{"type":"Line","context":"comment","value":"// OK","line":11,"start":313,"end":318},{"type":"T_CONST","context":"normal","value":"const","line":12,"start":319,"end":324},{"type":"T_IDENTIFIER","context":"normal","value":"person5","line":12,"start":325,"end":332},{"type":"T_COLON","context":"type","value":":","line":12,"start":332,"end":333},{"type":"T_IDENTIFIER","context":"type","value":"PersonDetails","line":12,"start":334,"end":347},{"type":"T_ASSIGN","context":"normal","value":"=","line":12,"start":348,"end":349},{"type":"T_LCURLY","context":"normal","value":"{","line":12,"start":350,"end":351},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":12,"start":351,"end":354},{"type":"T_COLON","context":"normal","value":":","line":12,"start":354,"end":355},{"type":"T_NUMBER","context":"normal","value":"28","line":12,"start":356,"end":358},{"type":"T_COMMA","context":"normal","value":",","line":12,"start":358,"end":359},{"type":"T_IDENTIFIER","context":"normal","value":"name","line":12,"start":360,"end":364},{"type":"T_COLON","context":"normal","value":":","line":12,"start":364,"end":365},{"type":"T_STRING","context":"normal","value":"'a'","line":12,"start":366,"end":369},{"type":"T_RCURLY","context":"normal","value":"}","line":12,"start":369,"end":370},{"type":"T_SEMICOLON","context":"normal","value":";","line":12,"start":370,"end":371},{"type":"Line","context":"comment","value":"// OK","line":12,"start":373,"end":378},{"type":"T_CONST","context":"normal","value":"const","line":13,"start":379,"end":384},{"type":"T_IDENTIFIER","context":"normal","value":"person6","line":13,"start":385,"end":392},{"type":"T_COLON","context":"type","value":":","line":13,"start":392,"end":393},{"type":"T_IDENTIFIER","context":"type","value":"PersonDetails","line":13,"start":394,"end":407},{"type":"T_ASSIGN","context":"normal","value":"=","line":13,"start":408,"end":409},{"type":"T_LCURLY","context":"normal","value":"{","line":13,"start":410,"end":411},{"type":"T_IDENTIFIER","context":"normal","value":"age","line":13,"start":411,"end":414},{"type":"T_COLON","context":"normal","value":":","line":13,"start":414,"end":415},{"type":"T_STRING","context":"normal","value":"'28'","line":13,"start":416,"end":420},{"type":"T_RCURLY","context":"normal","value":"}","line":13,"start":420,"end":421},{"type":"T_SEMICOLON","context":"normal","value":";","line":13,"start":421,"end":422},{"type":"Line","context":"comment","value":"// Error: string is incompatible with number","line":13,"start":424,"end":468}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot assign object literal to `person1` because property `name` is missing in object literal [1] but exists in `Person` [2]. [prop-missing]","context":"const person1: Person = {age: 28}; // Error: missing `name`","source":"-","start":{"line":8,"column":25,"offset":120},"end":{"line":8,"column":33,"offset":129}}],"operation":null},{"id":"E2","messages":[{"id":"E2M1","description":"Cannot assign object literal to `person2` because property `age` is missing in object literal [1] but exists in `Person` [2]. [prop-missing]","context":"const person2: Person = {name: 'a'}; // Error: missing `age`","source":"-","start":{"line":9,"column":25,"offset":181},"end":{"line":9,"column":35,"offset":192}}],"operation":null},{"id":"E3","messages":[{"id":"E3M1","description":"Cannot assign object literal to `person6` because string [1] is incompatible with number [2] in property `age`. [incompatible-type]","context":"const person6: PersonDetails = {age: '28'}; // Error: string is incompatible with number","source":"-","start":{"line":13,"column":32,"offset":410},"end":{"line":13,"column":42,"offset":421}}],"operation":null}]}
Note: $Shape<T>
is not equivalent to T
with all its fields marked as optional.
In particular, Flow unsoundly allows $Shape<T>
to be used as a T
in several
contexts. For example in
const personShape: PersonDetails = {age: 28};
(personShape: Person);
Flow will unsoundly allow this last cast to succeed. If this behavior is not wanted,
then this utility type should be avoided.
$Exports<T>
The following are functionally equivalent
1
|
import typeof * as T from 'my-module';
|
{"value":"import typeof * as T from 'my-module';\n","tokens":[{"type":"T_IMPORT","context":"normal","value":"import","line":1,"start":0,"end":6},{"type":"T_TYPEOF","context":"normal","value":"typeof","line":1,"start":7,"end":13},{"type":"T_MULT","context":"normal","value":"*","line":1,"start":14,"end":15},{"type":"T_IDENTIFIER","context":"normal","value":"as","line":1,"start":16,"end":18},{"type":"T_IDENTIFIER","context":"type","value":"T","line":1,"start":19,"end":20},{"type":"T_IDENTIFIER","context":"normal","value":"from","line":1,"start":21,"end":25},{"type":"T_STRING","context":"normal","value":"'my-module'","line":1,"start":26,"end":37},{"type":"T_SEMICOLON","context":"normal","value":";","line":1,"start":37,"end":38}],"errors":[]}
1
|
type T = $Exports<'my-module'>;
|
{"value":"type T = $Exports<'my-module'>;\n","tokens":[{"type":"T_TYPE","context":"normal","value":"type","line":1,"start":0,"end":4},{"type":"T_IDENTIFIER","context":"type","value":"T","line":1,"start":5,"end":6},{"type":"T_ASSIGN","context":"type","value":"=","line":1,"start":7,"end":8},{"type":"T_IDENTIFIER","context":"type","value":"$Exports","line":1,"start":9,"end":17},{"type":"T_LESS_THAN","context":"type","value":"<","line":1,"start":17,"end":18},{"type":"T_STRING","context":"type","value":"'my-module'","line":1,"start":18,"end":29},{"type":"T_GREATER_THAN","context":"type","value":">","line":1,"start":29,"end":30},{"type":"T_SEMICOLON","context":"normal","value":";","line":1,"start":30,"end":31}],"errors":[]}
The advantage of the $Exports
syntax is that you can export
the type on the same line
1
|
export type T = $Exports<'my-module'>;
|
{"value":"export type T = $Exports<'my-module'>;\n","tokens":[{"type":"T_EXPORT","context":"normal","value":"export","line":1,"start":0,"end":6},{"type":"T_TYPE","context":"normal","value":"type","line":1,"start":7,"end":11},{"type":"T_IDENTIFIER","context":"type","value":"T","line":1,"start":12,"end":13},{"type":"T_ASSIGN","context":"type","value":"=","line":1,"start":14,"end":15},{"type":"T_IDENTIFIER","context":"type","value":"$Exports","line":1,"start":16,"end":24},{"type":"T_LESS_THAN","context":"type","value":"<","line":1,"start":24,"end":25},{"type":"T_STRING","context":"type","value":"'my-module'","line":1,"start":25,"end":36},{"type":"T_GREATER_THAN","context":"type","value":">","line":1,"start":36,"end":37},{"type":"T_SEMICOLON","context":"normal","value":";","line":1,"start":37,"end":38}],"errors":[]}
where as you would otherwise need to export an alias in the import typeof
case
1
2
|
import typeof * as T from 'my-module';
export type MyModuleType = T;
|
{"value":"import typeof * as T from 'my-module';\nexport type MyModuleType = T;\n","tokens":[{"type":"T_IMPORT","context":"normal","value":"import","line":1,"start":0,"end":6},{"type":"T_TYPEOF","context":"normal","value":"typeof","line":1,"start":7,"end":13},{"type":"T_MULT","context":"normal","value":"*","line":1,"start":14,"end":15},{"type":"T_IDENTIFIER","context":"normal","value":"as","line":1,"start":16,"end":18},{"type":"T_IDENTIFIER","context":"type","value":"T","line":1,"start":19,"end":20},{"type":"T_IDENTIFIER","context":"normal","value":"from","line":1,"start":21,"end":25},{"type":"T_STRING","context":"normal","value":"'my-module'","line":1,"start":26,"end":37},{"type":"T_SEMICOLON","context":"normal","value":";","line":1,"start":37,"end":38},{"type":"T_EXPORT","context":"normal","value":"export","line":2,"start":39,"end":45},{"type":"T_TYPE","context":"normal","value":"type","line":2,"start":46,"end":50},{"type":"T_IDENTIFIER","context":"type","value":"MyModuleType","line":2,"start":51,"end":63},{"type":"T_ASSIGN","context":"type","value":"=","line":2,"start":64,"end":65},{"type":"T_IDENTIFIER","context":"type","value":"T","line":2,"start":66,"end":67},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":67,"end":68}],"errors":[]}
$Supertype<T>
This utility has been deprecated and should be avoided. See here for details.
$Subtype<T>
This utility has been deprecated and should be avoided. See here for details.
Existential Type (*
)
This utility has been deprecated and should be avoided. See here for details.