In general, programs have several different categories of types:
A single type:
Here the input value can only be a number
.
1
2
3
|
function square(n: number) {
return n * n;
}
|
{"value":"function square(n: number) {\n return n * n;\n}\n","tokens":[{"type":"T_FUNCTION","context":"normal","value":"function","line":1,"start":0,"end":8},{"type":"T_IDENTIFIER","context":"normal","value":"square","line":1,"start":9,"end":15},{"type":"T_LPAREN","context":"normal","value":"(","line":1,"start":15,"end":16},{"type":"T_IDENTIFIER","context":"normal","value":"n","line":1,"start":16,"end":17},{"type":"T_COLON","context":"type","value":":","line":1,"start":17,"end":18},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":1,"start":19,"end":25},{"type":"T_RPAREN","context":"normal","value":")","line":1,"start":25,"end":26},{"type":"T_LCURLY","context":"normal","value":"{","line":1,"start":27,"end":28},{"type":"T_RETURN","context":"normal","value":"return","line":2,"start":31,"end":37},{"type":"T_IDENTIFIER","context":"normal","value":"n","line":2,"start":38,"end":39},{"type":"T_MULT","context":"normal","value":"*","line":2,"start":40,"end":41},{"type":"T_IDENTIFIER","context":"normal","value":"n","line":2,"start":42,"end":43},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":43,"end":44},{"type":"T_RCURLY","context":"normal","value":"}","line":3,"start":45,"end":46}],"errors":[]}
A group of different possible types:
Here the input value could be either a string
or a number
.
1
2
3
|
function stringifyBasicValue(value: string | number) {
return '' + value;
}
|
{"value":"function stringifyBasicValue(value: string | number) {\n return '' + value;\n}\n","tokens":[{"type":"T_FUNCTION","context":"normal","value":"function","line":1,"start":0,"end":8},{"type":"T_IDENTIFIER","context":"normal","value":"stringifyBasicValue","line":1,"start":9,"end":28},{"type":"T_LPAREN","context":"normal","value":"(","line":1,"start":28,"end":29},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":1,"start":29,"end":34},{"type":"T_COLON","context":"type","value":":","line":1,"start":34,"end":35},{"type":"T_STRING_TYPE","context":"type","value":"string","line":1,"start":36,"end":42},{"type":"T_BIT_OR","context":"type","value":"|","line":1,"start":43,"end":44},{"type":"T_NUMBER_TYPE","context":"type","value":"number","line":1,"start":45,"end":51},{"type":"T_RPAREN","context":"normal","value":")","line":1,"start":51,"end":52},{"type":"T_LCURLY","context":"normal","value":"{","line":1,"start":53,"end":54},{"type":"T_RETURN","context":"normal","value":"return","line":2,"start":57,"end":63},{"type":"T_STRING","context":"normal","value":"''","line":2,"start":64,"end":66},{"type":"T_PLUS","context":"normal","value":"+","line":2,"start":67,"end":68},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":2,"start":69,"end":74},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":74,"end":75},{"type":"T_RCURLY","context":"normal","value":"}","line":3,"start":76,"end":77}],"errors":[]}
A type based on another type:
Here the return type will be the same as the type of whatever value is passed
into the function.
1
2
3
|
function identity<T>(value: T): T {
return value;
}
|
{"value":"function identity<T>(value: T): T {\n return value;\n}\n","tokens":[{"type":"T_FUNCTION","context":"normal","value":"function","line":1,"start":0,"end":8},{"type":"T_IDENTIFIER","context":"normal","value":"identity","line":1,"start":9,"end":17},{"type":"T_LESS_THAN","context":"type","value":"<","line":1,"start":17,"end":18},{"type":"T_IDENTIFIER","context":"type","value":"T","line":1,"start":18,"end":19},{"type":"T_GREATER_THAN","context":"type","value":">","line":1,"start":19,"end":20},{"type":"T_LPAREN","context":"normal","value":"(","line":1,"start":20,"end":21},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":1,"start":21,"end":26},{"type":"T_COLON","context":"type","value":":","line":1,"start":26,"end":27},{"type":"T_IDENTIFIER","context":"type","value":"T","line":1,"start":28,"end":29},{"type":"T_RPAREN","context":"normal","value":")","line":1,"start":29,"end":30},{"type":"T_COLON","context":"type","value":":","line":1,"start":30,"end":31},{"type":"T_IDENTIFIER","context":"type","value":"T","line":1,"start":32,"end":33},{"type":"T_LCURLY","context":"normal","value":"{","line":1,"start":34,"end":35},{"type":"T_RETURN","context":"normal","value":"return","line":2,"start":38,"end":44},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":2,"start":45,"end":50},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":50,"end":51},{"type":"T_RCURLY","context":"normal","value":"}","line":3,"start":52,"end":53}],"errors":[]}
These three are the most common categories of types. They will make up the
majority of the types you’ll be writing.
However, there is also a fourth category.
An arbitrary type that could be anything:
Here the passed in value is an unknown type, it could be any type and the
function would still work.
1
2
3
|
function getTypeOf(value: mixed): string {
return typeof value;
}
|
{"value":"function getTypeOf(value: mixed): string {\n return typeof value;\n}\n","tokens":[{"type":"T_FUNCTION","context":"normal","value":"function","line":1,"start":0,"end":8},{"type":"T_IDENTIFIER","context":"normal","value":"getTypeOf","line":1,"start":9,"end":18},{"type":"T_LPAREN","context":"normal","value":"(","line":1,"start":18,"end":19},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":1,"start":19,"end":24},{"type":"T_COLON","context":"type","value":":","line":1,"start":24,"end":25},{"type":"T_MIXED_TYPE","context":"type","value":"mixed","line":1,"start":26,"end":31},{"type":"T_RPAREN","context":"normal","value":")","line":1,"start":31,"end":32},{"type":"T_COLON","context":"type","value":":","line":1,"start":32,"end":33},{"type":"T_STRING_TYPE","context":"type","value":"string","line":1,"start":34,"end":40},{"type":"T_LCURLY","context":"normal","value":"{","line":1,"start":41,"end":42},{"type":"T_RETURN","context":"normal","value":"return","line":2,"start":45,"end":51},{"type":"T_TYPEOF","context":"normal","value":"typeof","line":2,"start":52,"end":58},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":2,"start":59,"end":64},{"type":"T_SEMICOLON","context":"normal","value":";","line":2,"start":64,"end":65},{"type":"T_RCURLY","context":"normal","value":"}","line":3,"start":66,"end":67}],"errors":[]}
These unknown types are less common, but are still useful at times.
You should represent these values with mixed
.
Anything goes in, Nothing comes out
mixed
will accept any type of value. Strings, numbers, objects, functions–
anything will work.
1
2
3
4
5
6
7
8
9
|
function stringify(value: mixed) {
}
stringify("foo");
stringify(3.14);
stringify(null);
stringify({});
|
{"value":"// @flow\nfunction stringify(value: mixed) {\n // ...\n}\n\nstringify(\"foo\");\nstringify(3.14);\nstringify(null);\nstringify({});\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":"stringify","line":2,"start":18,"end":27},{"type":"T_LPAREN","context":"normal","value":"(","line":2,"start":27,"end":28},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":2,"start":28,"end":33},{"type":"T_COLON","context":"type","value":":","line":2,"start":33,"end":34},{"type":"T_MIXED_TYPE","context":"type","value":"mixed","line":2,"start":35,"end":40},{"type":"T_RPAREN","context":"normal","value":")","line":2,"start":40,"end":41},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":42,"end":43},{"type":"Line","context":"comment","value":"// ...","line":3,"start":46,"end":52},{"type":"T_RCURLY","context":"normal","value":"}","line":4,"start":53,"end":54},{"type":"T_IDENTIFIER","context":"normal","value":"stringify","line":6,"start":56,"end":65},{"type":"T_LPAREN","context":"normal","value":"(","line":6,"start":65,"end":66},{"type":"T_STRING","context":"normal","value":"\"foo\"","line":6,"start":66,"end":71},{"type":"T_RPAREN","context":"normal","value":")","line":6,"start":71,"end":72},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":72,"end":73},{"type":"T_IDENTIFIER","context":"normal","value":"stringify","line":7,"start":74,"end":83},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":83,"end":84},{"type":"T_NUMBER","context":"normal","value":"3.14","line":7,"start":84,"end":88},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":88,"end":89},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":89,"end":90},{"type":"T_IDENTIFIER","context":"normal","value":"stringify","line":8,"start":91,"end":100},{"type":"T_LPAREN","context":"normal","value":"(","line":8,"start":100,"end":101},{"type":"T_NULL","context":"normal","value":"null","line":8,"start":101,"end":105},{"type":"T_RPAREN","context":"normal","value":")","line":8,"start":105,"end":106},{"type":"T_SEMICOLON","context":"normal","value":";","line":8,"start":106,"end":107},{"type":"T_IDENTIFIER","context":"normal","value":"stringify","line":9,"start":108,"end":117},{"type":"T_LPAREN","context":"normal","value":"(","line":9,"start":117,"end":118},{"type":"T_LCURLY","context":"normal","value":"{","line":9,"start":118,"end":119},{"type":"T_RCURLY","context":"normal","value":"}","line":9,"start":119,"end":120},{"type":"T_RPAREN","context":"normal","value":")","line":9,"start":120,"end":121},{"type":"T_SEMICOLON","context":"normal","value":";","line":9,"start":121,"end":122}],"errors":[]}
When you try to use a value of a mixed
type you must first figure out what
the actual type is or you’ll end up with an error.
1
2
3
4
5
6
7
|
function stringify(value: mixed) {
return "" + value;
}
stringify("foo");
|
Cannot add empty string and `value` because mixed [1] could either behave like a string or like a number. [unclear-addition]
{"value":"// @flow\nfunction stringify(value: mixed) {\n // $ExpectError\n return \"\" + value; // Error!\n}\n\nstringify(\"foo\");\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":"stringify","line":2,"start":18,"end":27},{"type":"T_LPAREN","context":"normal","value":"(","line":2,"start":27,"end":28},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":2,"start":28,"end":33},{"type":"T_COLON","context":"type","value":":","line":2,"start":33,"end":34},{"type":"T_MIXED_TYPE","context":"type","value":"mixed","line":2,"start":35,"end":40},{"type":"T_RPAREN","context":"normal","value":")","line":2,"start":40,"end":41},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":42,"end":43},{"type":"Line","context":"comment","value":"// $ExpectError","line":3,"start":46,"end":61},{"type":"T_RETURN","context":"normal","value":"return","line":4,"start":64,"end":70},{"type":"T_STRING","context":"normal","value":"\"\"","line":4,"start":71,"end":73},{"type":"T_PLUS","context":"normal","value":"+","line":4,"start":74,"end":75},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":4,"start":76,"end":81},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":81,"end":82},{"type":"Line","context":"comment","value":"// Error!","line":4,"start":83,"end":92},{"type":"T_RCURLY","context":"normal","value":"}","line":5,"start":93,"end":94},{"type":"T_IDENTIFIER","context":"normal","value":"stringify","line":7,"start":96,"end":105},{"type":"T_LPAREN","context":"normal","value":"(","line":7,"start":105,"end":106},{"type":"T_STRING","context":"normal","value":"\"foo\"","line":7,"start":106,"end":111},{"type":"T_RPAREN","context":"normal","value":")","line":7,"start":111,"end":112},{"type":"T_SEMICOLON","context":"normal","value":";","line":7,"start":112,"end":113}],"errors":[{"id":"E1","messages":[{"id":"E1M1","description":"Cannot add empty string and `value` because mixed [1] could either behave like a string or like a number. [unclear-addition]","context":" return \"\" + value; // Error!","source":"-","start":{"line":4,"column":15,"offset":76},"end":{"line":4,"column":19,"offset":81}}],"operation":null}]}
Instead you must ensure the value is a certain type by refining it.
1
2
3
4
5
6
7
8
9
10
|
function stringify(value: mixed) {
if (typeof value === 'string') {
return "" + value;
} else {
return "";
}
}
stringify("foo");
|
{"value":"// @flow\nfunction stringify(value: mixed) {\n if (typeof value === 'string') {\n return \"\" + value; // Works!\n } else {\n return \"\";\n }\n}\n\nstringify(\"foo\");\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":"stringify","line":2,"start":18,"end":27},{"type":"T_LPAREN","context":"normal","value":"(","line":2,"start":27,"end":28},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":2,"start":28,"end":33},{"type":"T_COLON","context":"type","value":":","line":2,"start":33,"end":34},{"type":"T_MIXED_TYPE","context":"type","value":"mixed","line":2,"start":35,"end":40},{"type":"T_RPAREN","context":"normal","value":")","line":2,"start":40,"end":41},{"type":"T_LCURLY","context":"normal","value":"{","line":2,"start":42,"end":43},{"type":"T_IF","context":"normal","value":"if","line":3,"start":46,"end":48},{"type":"T_LPAREN","context":"normal","value":"(","line":3,"start":49,"end":50},{"type":"T_TYPEOF","context":"normal","value":"typeof","line":3,"start":50,"end":56},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":3,"start":57,"end":62},{"type":"T_STRICT_EQUAL","context":"normal","value":"===","line":3,"start":63,"end":66},{"type":"T_STRING","context":"normal","value":"'string'","line":3,"start":67,"end":75},{"type":"T_RPAREN","context":"normal","value":")","line":3,"start":75,"end":76},{"type":"T_LCURLY","context":"normal","value":"{","line":3,"start":77,"end":78},{"type":"T_RETURN","context":"normal","value":"return","line":4,"start":83,"end":89},{"type":"T_STRING","context":"normal","value":"\"\"","line":4,"start":90,"end":92},{"type":"T_PLUS","context":"normal","value":"+","line":4,"start":93,"end":94},{"type":"T_IDENTIFIER","context":"normal","value":"value","line":4,"start":95,"end":100},{"type":"T_SEMICOLON","context":"normal","value":";","line":4,"start":100,"end":101},{"type":"Line","context":"comment","value":"// Works!","line":4,"start":102,"end":111},{"type":"T_RCURLY","context":"normal","value":"}","line":5,"start":114,"end":115},{"type":"T_ELSE","context":"normal","value":"else","line":5,"start":116,"end":120},{"type":"T_LCURLY","context":"normal","value":"{","line":5,"start":121,"end":122},{"type":"T_RETURN","context":"normal","value":"return","line":6,"start":127,"end":133},{"type":"T_STRING","context":"normal","value":"\"\"","line":6,"start":134,"end":136},{"type":"T_SEMICOLON","context":"normal","value":";","line":6,"start":136,"end":137},{"type":"T_RCURLY","context":"normal","value":"}","line":7,"start":140,"end":141},{"type":"T_RCURLY","context":"normal","value":"}","line":8,"start":142,"end":143},{"type":"T_IDENTIFIER","context":"normal","value":"stringify","line":10,"start":145,"end":154},{"type":"T_LPAREN","context":"normal","value":"(","line":10,"start":154,"end":155},{"type":"T_STRING","context":"normal","value":"\"foo\"","line":10,"start":155,"end":160},{"type":"T_RPAREN","context":"normal","value":")","line":10,"start":160,"end":161},{"type":"T_SEMICOLON","context":"normal","value":";","line":10,"start":161,"end":162}],"errors":[]}
Because of the typeof value === 'string'
check, Flow knows the value
can
only be a string
inside of the if
statement. This is known as a
refinement.