Skip to content

Commit

Permalink
Documentation for Class<T>, $PropertyType<T, x>, $Exact<T>, and *
Browse files Browse the repository at this point in the history
Summary:
Ref: #2464
Closes #2983

Reviewed By: gabelevi

Differential Revision: D4401030

Pulled By: samwgoldman

fbshipit-source-id: 9411d762e307d97c56b80005c00d866f1fc74eed
  • Loading branch information
STRML authored and facebook-github-bot committed Feb 28, 2017
1 parent edc0fef commit e400af9
Showing 1 changed file with 148 additions and 7 deletions.
155 changes: 148 additions & 7 deletions website/docs/ref/utility-types.doc.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@ next: react.html
---
*/

/*
# Utility Types
*/

/*
## `$Keys<T>`
In Flow you can [use union types similar to enums](builtins.html#literal-types):
Expand Down Expand Up @@ -44,6 +40,7 @@ function printSuitNumber(suit: Suit) {
}

printSuitNumber('Diamonds'); // 2
// $ExpectError
printSuitNumber('foo'); // 'foo' is not a Suit

/*
Expand Down Expand Up @@ -91,8 +88,41 @@ As you may have noticed, the example is not a random one. `$Diff` is exactly wha

/*
## `Class<T>`
Work in progress
Given a type `T` representing instances of a class `C`, the type `Class<T>` is the type of the class `C`.
For example:
*/

class Store {}
class ExtendedStore extends Store {}
class Model {}

function makeStore(storeClass: Class<Store>) {
return new storeClass();
}

(makeStore(Store): Store);
(makeStore(ExtendedStore): Store);
// $ExpectError
(makeStore(Model): Model); // error, Class<Model> does not satisfy Class<Store>
// $ExpectError
(makeStore(ExtendedStore): Model); // Flow infers the return type

/*
For classes that take type parameters, you must also provide the parameter. For example:
*/

class ParamStore<T> {
constructor(data: T) {}
}

function makeParamStore<T>(storeClass: Class<ParamStore<T>>, data: T): ParamStore<T> {
return new storeClass(data);
}
(makeParamStore(ParamStore, 1): ParamStore<number>);
// $ExpectError
(makeParamStore(ParamStore, 1): ParamStore<boolean>); // failed because of the second parameter

/*
## `$Supertype<T>`
Work in progress

Expand All @@ -102,6 +132,117 @@ Work in progress
## `$Abstract<T>`
Work in progress

## `#PropertyType<T, x>`
Work in progress
## `$PropertyType<T, x>`
A $PropertyType is the type at a given key.

As of Flow v0.36.0, `x` must be a literal string. In future versions, `x` may be allowed to be any type, as long
as that type exists on the keys of `T`.
*/

type Person = {
name: string,
age: number,
parent: Person
};

const newName: $PropertyType<Person, 'name'> = 'Michael Jackson';
const newAge: $PropertyType<Person, 'age'> = 50;
// $ExpectError
const newParent: $PropertyType<Person, 'parent'> = 'Joe Jackson';

/*
This can be especially useful for referring to the type of React props, or, even the entire `props` type itself.
*/

import React from 'react';
class Tooltip extends React.Component {
props: {
text: string,
onMouseOver: ({x: number, y: number}) => void
};
}

const someProps: $PropertyType<Tooltip, 'props'> = {
text: 'foo',
onMouseOver: (data: {x: number, y: number}) => undefined
};

// $ExpectError
const otherProps: $PropertyType<Tooltip, 'props'> = {
text: 'foo'
// Missing the `onMouseOver` definition
};

/*
You can even nest lookups:
*/
type PositionHandler = $PropertyType<$PropertyType<Tooltip, 'props'>, 'onMouseOver'>;
const handler: PositionHandler = (data: {x: number, y: number}) => undefined;
// $ExpectError
const handler2: PositionHandler = (data: string) => undefined; // wrong parameter types

/*
You can use this in combination with `Class<T>` to get static props:
*/

class BackboneModel {
static idAttribute: string | false;
}

type ID = $PropertyType<Class<BackboneModel>, 'idAttribute'>;
const someID: ID = '1234';
// $ExpectError
const someBadID: ID = true;

/*
## `*`
`*` is known as the existential type.
An existential type is used as a placeholder to tell Flow to infer the type.
For example, in the `Class<ParamStore<T>>` example, we could have used an existential type for the return:
*/
function makeParamStore<T>(storeClass: Class<ParamStore<T>>, data: T): * {
return new storeClass(data);
}
(makeParamStore(ParamStore, 1): ParamStore<number>);
// $ExpectError
(makeParamStore(ParamStore, 1): ParamStore<boolean>); // failed because of the second parameter

/*
The `*` can be thought of as an "auto" instruction to Flow, telling it to fill in the type from context.

In comparison to `any`, `*` may allow you to avoid losing type safety.

The existential operator is also useful for automatically filling in types without unnecessary verbosity:
*/

class DataStore {
data: *; // If this property weren't defined, you'd get an error just trying to assign `data`
constructor() {
this.data = {
name: 'DataStore',
isOffline: true
};
}
goOnline() {
this.data.isOffline = false;
}
changeName() {
// $ExpectError
this.data.isOffline = 'SomeStore'; // oops, wrong key!
}
}

/*
## `$Exact<T>`
`$Exact<{name: string}>` is a synonym for `{| name: string |}` as in the [Object documentation](objects.html#exact-object-types).
*/

type ExactUser = $Exact<{name: string}>;
type ExactUserShorthand = {| name: string |};

const user2 = {name: 'John Wilkes Booth'};
// These will both be satisified because they are equivalent
(user2: ExactUser);
(user2: ExactUserShorthand);

0 comments on commit e400af9

Please sign in to comment.