Secret Flow Types

Roman Krivtsov
Netscape
Published in
4 min readJan 23, 2017

Of course they are not secret. Recently Flow maintainers exposed some advanced and experimental features, which were previously not documented and used for internal needs. Some of them you probably already came across in articles, issues or source code, but some of them are still not documented.

In this post I’ve tried to collect all utility types, that I found and got with their meanings. Despite that API of these features can (likely) change and you should use them in your projects with caution, some of them are pretty useful in practice.

$Keys<T>

Accepts object type and returns the new type, which is basically union of it’s keys. For example we can get tuple of keys in object type.

type O = {
a: string,
b: string
}
const v: $Keys<O> = 12; // error
const a: [$Keys<O>] = ['a', 'b', 'c']; // error
const b: [$Keys<O>] = ['a']; // ok

$Diff<A, B>

Diff does exactly that you expect it will do. The result type is an object type, which contains only properties, which exist in object type A, but don’t in type B. Good example of usage is default options:

type AllOptions = { delimiter: string, quotes: string};
type Defaults = { quotes: string, newLine: string };
type Required = $Diff<AllOptions, Defaults>;
const options: Required = { // error: delimiter property not found
quotes: '"',
newLine: '\n'
}

$Shape<T>

Type $Shape<T> has two differences from T: objects of this type cannot contain properties not described in T and may not contain required properties of type T.

type T = {
foo: string,
bar: number
}
const a: $Shape<T> = {
foo: '121', // no errors about not having bar
baz: true // error, baz is not in T type
}

You can think of $Shape<T> and T as “overlapping” types.

$Supertype<T>

$Supertype represents supertype of type T. But implementation details of this type are not clear yet, so using of it can lead to mistakes. For example it doesn’t raise errors when it should:

class A {}
class B extends A {}
class C {}
const a1:$Supertype<B> = new A; // no error
const a1:$Supertype<A> = new B; // no error
const a3:$Supertype<A> = new C; // no error

$Subtype<T>

$Subtypes looks better. At least more predictable:

class A {}
class B extends A {}
class C {}
const a1:$Subtype<A> = new B;
const a2:$Subtype<A> = new C; // error

Class<T>

Represents class type, whose instance has type T. For some reasons it doesn’t have $ sign in the name. So if you called your own type by Class name it won’t work as expected.

Note also that Class is covariant:

class A {}
class B extends A {}
const a = new A;
const b = new B;
const c: Class<typeof a> = B; // no error
const d: Class<typeof b> = A; // error

$PropertyType<T, x>

$PropertyType is a useful thing. Let’s assume, that we automatically generated type from some model:

type User = {
name: string,
age: number,
contacts: {
phone: string,
email: string,
address: string
}
}

But now we need type Contacts somewhere. Instead of copypasting we can use $PropertyType:

type Contacts = $PropertyType<User, 'contacts'>;// error: address is missing
const a: Contacts = {phone: '32423', email: 'homer@simpson.com'}

$Pred/$Ref

These types are about function predicates. $Pred refines arguments count of function and $Ref refines position of certain type in arguments.

type User = {name: string}
function refine<P: $Pred<1>>(user: User, cb: P): $Refine<User,P,1>{
// error. User is expecting on 1 position
if (cb(null, user)) return user;
}

But there is very little information about these types and complete behavior is also not clear, so I wouldn’t recommend to use them.

$Exact<T>

Describes type, that is exactly the same as T. It can be useful, because usually in object types extra properties are allowed:

type T = {
a: string,
b: string
}
const a: $Exact<T> = {
a: 'abc',
b: 'def',
c: 12 // error
}
const b: T = {
a: 'abc',
b: 'def',
c: 12 // no error
}

This behavior is similar to {||} syntax described in docs

$ObjMap<T, F>

$ObjMap allows to map your object type values. It’s not so powerful as new mapped types in TypeScript, but hopefully will be more advanced in future.

Let’s assume that we have type of HTTP request parameters.

type Params = {
user: {login: string, pass: string},
referrer: string
}

And we’d like to define handlers for these params. For example user object should be retrieved from body and referrer is from query string.

type Handler = (req: any, paramName: string) => {}
const bodyHandler: Handler = (req, param) => req.body[param]
const queryStringHandler: Handler = (req, param) => req.qs[param]
type ParamsHandlers = $ObjMap<Params, <V>(v: any) => Handler>
const handlers: ParamsHandlers = {
user: bodyHandler,
referrer: queryStringHandler
}

Here $ObjMap helps us to map type values to function type to validate new handlers object.

There are also $TupleMap for tuples and $ObjMapi, which at least at this particular example works similarly.

Find me in Twitter

--

--