Skip to main content

Typeof Types

JavaScript has a typeof operator which returns a string describing a value.

1typeof 1 === 'number'2typeof true === 'boolean'3typeof 'three' === 'string'

However it is limited in that this string only describes so much about the type.

1typeof {foo: true} === 'object'2typeof null === 'object'3typeof [true, false] === 'object'

In Flow, there is a similar typeof type operator, but it's much more powerful.

typeof type syntax

The typeof operator returns the Flow type of a given value to be used as a type.

1let num1 = 42;2let num2: typeof num1 = 3.14;     // Works!3let num3: typeof num1 = 'world';  // Error!
4 5let bool1 = true;6let bool2: typeof bool1 = false; // Works!7let bool3: typeof bool1 = 42; // Error!
8 9let str1 = 'hello';10let str2: typeof str1 = 'world'; // Works!11let str3: typeof str1 = false; // Error!
3:25-3:31: Cannot assign `'world'` to `num3` because string [1] is incompatible with number [2]. [incompatible-type]
7:27-7:28: Cannot assign `42` to `bool3` because number [1] is incompatible with boolean [2]. [incompatible-type]
11:25-11:29: Cannot assign `false` to `str3` because boolean [1] is incompatible with string [2]. [incompatible-type]

You can use any value with typeof, as long as the arugment itself is a variable or member access:

1let obj1 = {foo: 1, bar: true, baz: 'three'};2let obj2: typeof obj1 = {foo: 42, bar: false, baz: 'hello'};3let num: typeof obj1.bar = 1;4
5let arr1 = [1, 2, 3];6let arr2: typeof arr1 = [3, 2, 1];7
8type T = typeof {a: 1}; // Invalid!
8:17-8:17: `typeof` can only be used to get the type of variables.

typeof inherits behaviors of inference

When you use typeof, you're taking the results of Flow's inference and asserting it as a type. While this can be very useful, it can also lead to some unexpected results.

For example, when you use literal values in Flow, their inferred type is the primitive that it belongs to. Thus, the number 42 has the inferred type of number. You can see this when you use typeof.

1let num1 = 42;2let num2: typeof num1 = 3.14;    // Works!3
4let bool1 = true;5let bool2: typeof bool1 = false; // Works!6
7let str1 = 'hello';8let str2: typeof str1 = 'world'; // Works!

However, this only happens with the inferred type. If you specify the literal type, it will be used in typeof.

1let num1: 42 = 42;2let num2: typeof num1 = 3.14;    // Error!
3 4let bool1: true = true;5let bool2: typeof bool1 = false; // Error!
6 7let str1: 'hello' = 'hello';8let str2: typeof str1 = 'world'; // Error!
2:25-2:28: Cannot assign `3.14` to `num2` because number [1] is incompatible with number literal `42` [2]. [incompatible-type]
5:27-5:31: Cannot assign `false` to `bool2` because boolean [1] is incompatible with boolean literal `true` [2]. [incompatible-type]
8:25-8:31: Cannot assign `'world'` to `str2` because string [1] is incompatible with string literal `hello` [2]. [incompatible-type]

typeof inherits behaviors of other types

There are many different types in Flow, some of these types behave differently than others. These differences make sense for that particular type but not for others.

When you use typeof, you're inserting another type with all of its behaviors. This can make typeof seem inconsistent where it is not.

For example, if you use typeof with a class you need to remember that classes are nominally typed instead of structurally typed. So that two classes with the same exact shape are not considered equivalent.

1class MyClass {2  method(val: number) { /* ... */ }3}4
5class YourClass {6  method(val: number) { /* ... */ }7}8
9let test1: typeof MyClass = YourClass; // Error!
10let test2: typeof MyClass = MyClass; // Works!
9:29-9:37: Cannot assign `YourClass` to `test1` because `YourClass` [1] is incompatible with `MyClass` [2]. [incompatible-type]