Type Casting Expressions
Sometimes it is useful to assert a type without using something like a function or a variable to do so. For this Flow supports an inline type cast expression syntax which can be used in a number of different ways.
Type Cast Expression Syntax
In order to create a type cast expression, use the keyword as
to cast the value to a type:
value as Type
This can also be referred to as an "as expression".
Before Flow version 0.229, the legacy syntax
(value: Type)
was used.
Type cast expressions can appear anywhere an expression can appear:
let val = value as Type;
let obj = {prop: value as Type};
let arr = [value as Type, value as Type] as Array<Type>;
The value itself can also be an expression:
12 + 2 as number;
Note that the as
operator has the same precedence as in
and instanceof
.
Because of this, parentheses around the expression might be required:
11 === 1 as boolean; // Error! 2// Above same as `1 === (1 as boolean)3
4(1 === 1) as boolean; // Works!
1:7-1:7: Cannot cast `1` to boolean because number [1] is incompatible with boolean [2]. [incompatible-cast]
Additionally, when in the context of an expression statement, expressions which could ambiguously parse as statements need parens:
1({a: 1}) as {a: number}; // Needs parens to disambiguate from block statement2const x = {a: 1} as {a: number}; // No parens needed, as not in expression statement context
When you strip the types all that is left is the value:
value as Type;
Is transformed into:
value;
Type Assertions
Using type cast expressions you can assert that values are certain types.
1let value = 42;2
3value as 42; // Works!4value as number; // Works!5value as string; // Error!
5:1-5:5: Cannot cast `value` to string because number [1] is incompatible with string [2]. [incompatible-cast]
Asserting types in this way works the same as types do anywhere else.
Type Casting
When you write a type cast expression, the result of that expression is the value with the provided type. If you hold onto the resulting value, it will have the new type.
1let value = 42;2
3value as 42; // Works!4value as number; // Works!5
6let newValue = value as number;7
8newValue as 42; // Error! 9newValue as number; // Works!
8:1-8:8: Cannot cast `newValue` to number literal `42` because number [1] is incompatible with number literal `42` [2]. [incompatible-cast]
Unsafe downcasts are not allowed:
1const fooObj = {foo: 1};2const otherObj = fooObj as {foo: number, bar: string}; // ERROR
2:18-2:23: Cannot cast `fooObj` to object type because property `bar` is missing in object literal [1] but exists in object type [2]. [prop-missing]
Adoption of as
syntax
To use the as
keyword for type casts, you need to upgrade your infrastructure so that it supports the syntax:
- Flow and Flow Parser: 0.229+
- Prettier: 3.1+
- Babel: use the babel-plugin-syntax-hermes-parser plugin version 0.19+, see our Babel guide for more details.
- ESLint: use hermes-eslint plugin version 0.19+, see our ESLint guide for more details.
For more details on how to migrate to the new casting syntax (as
) check out our blog post.
Using type cast expressions
Note: We're going to go through a stripped down example for demonstrating how to make use of type cast expressions. This example is not solved well in practice.
Type Casting through any
Because type casts work the same as all other type annotations, you can only cast values to less specific types. You cannot change the type or make it something more specific.
But you can use any to cast to whatever type you want.
1let value = 42;2
3value as number; // Works!4value as string; // Error! 5
6let newValue = value as any as string;7
8newValue as number; // Error! 9newValue as string; // Works!
4:1-4:5: Cannot cast `value` to string because number [1] is incompatible with string [2]. [incompatible-cast]8:1-8:8: Cannot cast `newValue` to number because string [1] is incompatible with number [2]. [incompatible-cast]
By casting the value to any
, you can then cast to whatever you want.
This is unsafe and not recommended. But it's sometimes useful when you are doing something with a value which is very difficult or impossible to type and want to make sure that the result has the desired type.
For example, the following function for cloning an object.
1function cloneObject(obj: any) {2 const clone: {[string]: mixed} = {};3
4 Object.keys(obj).forEach(key => {5 clone[key] = obj[key];6 });7
8 return clone;9}
It would be hard to create a type for this because we're creating a new object based on another object.
If we cast through any, we can return a type which is more useful.
1function cloneObject<T: {+[key: string]: mixed }>(obj: T): T {2 const clone: {[string]: mixed} = {};3
4 Object.keys(obj).forEach(key => {5 clone[key] = obj[key];6 });7
8 return clone as any as T;9}10
11const clone = cloneObject({12 foo: 1,13 bar: true,14 baz: 'three'15});16
17clone.foo as 1; // Works!18clone.bar as true; // Works!19clone.baz as 'three'; // Works!
Legacy casting syntax
Before version 0.229, to create a type cast expression around a value
, you would
add a colon :
with the Type
and wrap the expression with parentheses (
)
.
(value: Type)
Note: The parentheses are necessary to avoid ambiguity with other syntax.