2018-10-17
Both Flow and TypeScript provide several utility types. Here I've taken Flow's utility types and compiled some examples to show the equivalent syntax in TypeScript. The examples are (mostly) taken straight from the Flow docs.
Disclaimer: I don't use Flow. I may miss some less obvious features - please let me know if I do!
$Keys<T>🔗TypeScript just uses keyof, though you could define type $Keys<T> = keyof T
;
; // $Keys<typeof countries>
;
; // Error
$Values<T>🔗Easily defined as T[keyof T], can be wrapped as type $Values<T> = T[keyof T] if desired.
; // $Values<T>
;
// The following two types are equivalent:
;
;
; // OK, if in a module context
; // OK
; // Error
$ReadOnly<T>🔗Can be easily defined using mapped types. The TypeScript handbook actually provides it as an example!
;
;
;
$Exact<T>🔗$Exact<T> doesn't quite make sense in TypeScript. If you specify the type of an object when defining an object literal, you will get an error if you try to provide more properties.
;
; // Error
However, it may sometimes be desirable to check later. In this case, it is possible to check if two types are exactly equal, and cause a type error if they are not. The conditional-type-checks library makes this fairly painless. The AssertTrue and IsExactType types have been extracted from it for purposes of demonstration.
;
;
;
;
$Diff<A, B>🔗Diff in TypeScript is commonly used for union types, however the $Diff defined by Flow can easily be defined in TypeScript as well.
There are three similar ways to represent $Diff, each has its advantages. While $Diff2 below is closer to the diff used by Flow, the $Diff implementation below is safer.
;
;
;
;
;
;
;
;
;
;
; // Error
; // Ok, as in Flow
; // Error
; // Error
Note that the above $Diff implementations will not complain if you try to remove properties which do not exist in A. There isn't a standard way to raise a compile time type error in TypeScript, but it can be achieved by resolving the type to never and causing an error where we try to assign an object to never.
;
;
;
;
;
;
;
;
; // Still Ok
; // Error
However, this implementation still does not quite match Flow's $Diff type. In Flow's $Diff type, Flow will allow keys that don't exist in A to exist in B if they are defined with | void. TypeScript's equivalent is | undefined, so here's one last implementation.
;
;
;
;
;
;
;
;
;
; // Still Ok, other is optional
; // Error
$Rest<A, B>🔗$Rest is much simpler to represent with TypeScript. $Rest<A, B> returns an object with A's properties that are not B's properties. Following Flow's behavior, the remaining properties are marked optional.
;
;
;
$PropertyType<T, k>, $ElementType<T, K>🔗Like $Keys, there's not much point in defining this in TypeScript. You can just use T['key'].
;
;
;
;
; // Error
$NonMaybeType<T>🔗This can be trivially implemented using the built in Exclude.
;
;
; // => string
$ObjType<T, F>, $TupleMap<T, F>🔗This is a bit more complicated, TypeScript doesn't have the ability to "call" a function type like Flow does, but it should be possible to write types which provide the same behavior.
Here's a few examples, taken from the Flow docs and translated to TypeScript.
// Run
;
;
; // Ok
; // Ok
; // Error
; // Error, c doesn't exist
// Props
;
;
;
promises;
// Tuple Run
// Unfortunately this only works when arr functions return a single type.
;
;
;
;
; // Error
$Call<F>🔗It isn't currently possible to completely represent $Call with TypeScript as it requires support for variadic kinds, which has been on the roadmap for several versions. However, for functions with a static return type, it is possible to use the built in ReturnType<T> to extract a the return type of a function.
Class<T>🔗Is not quite possible in TypeScript, there's no way to recover the constructor type from any instance type. (T['constructor'] is just Function)
However, it is mostly unnecessary in TypeScript, just use typeof and the class name.
;
Store;
ExtendedStore;
// To make this error, add a property to Store that is not in Model.
Model; // No error
$Shape<T>🔗Is identical to Partial<T> in TypeScript.