Type Systems & Props Design 
 Exploring PropTypes, TypeScript, Flow & Reason
Let’s start without types
<Button primary={true} small={true} />
<Button primary small />
<Button secondary large />
<Button primary secondary small large />
<Button variant="primary" size="small" />
<Arrow down />
<Arrow up down />
<Arrow up down />
<Arrow direction="down" />
<Arrow direction="left" />
<Arrow direction="notRight"/>
A or B or C
easy to fix … is it though?
Type systems to the rescue!
<Arrow direction="up" /> PropTypes.string.isRequired;
PropTypes.string.isRequired; <Arrow direction="up" />
PropTypes.oneOf(["down", "up"]).isRequired; <Arrow direction="up" />
export enum Direction { Up = "up", Down = "down" } interface Props { direction: Direction; } import { Direction } from "./Arrow"; <Arrow direction={Direction.Left} />
interface Props { direction: "up" | "down"; } <Arrow direction="down" />
interface Props { direction: "up" | "down"; } <Arrow direction="down" />
<Arrow direction=Down /> type direction = Down | Up;
A{x} or B{x, y} or C
Draft{content}
 Post{content, publishedAt}
interface DraftPost { status: "draft"; content: String; } interface PublishedPost { status: "published"; content: String; publishedAt: Date; } type Props = DraftPost | PublishedPost;
export default function Post(props: Props) { switch (props.status) { case "draft": return <div>{props.content}</div>; case "published": return <div>{props.content}{props.publishedAt}</div>; } }
Discriminated Unions
Draft{status, content}
 Post{status, content, publishedAt}
interface DraftPost { status: "draft"; content: String; } interface PublishedPost { status: "published"; content: String; publishedAt: Date; } type Post = DraftPost | PublishedPost; interface Props { post: Post; }
<Post post={{ status: "published", content: "Unicorns & Cats", publishedAt: new Date() }} />
type DraftPost = { status: "draft", content: string }; type PublishedPost = { status: "published", content: string, publishedAt: Date }; type Props = DraftPost | PublishedPost;
export default function Post(props: Props) { switch (props.status) { case "draft": return <div>{props.content}</div>; case "published": return <div>{props.content}{props.publishedAt}</div>; default: return null; } }
type DraftPost = {| status: "draft", content: string |}; type PublishedPost = {| status: "published", content: string, publishedAt: Date |}; type Props = DraftPost | PublishedPost;
Disjoint Unions
Draft{status, content}
 Post{status, content, publishedAt}
<Post post={{ status: "published", content: "Unicorns & Cats", publishedAt: new Date() }} />
type post = | Draft(string) | Published(string, int);
let component = ReasonReact.statelessComponent("Arrow"); let make = (~post: post, _children) => { ...component, render: _self => switch (post) { | Draft(content) => <div> content->s </div> | Published(content, timestamp) => <div> content->s timestamp->string_of_int->s </div> }, };
Variants
Draft(content)
 Post(content, publishedAt)
Render Props
<Query<ProfileData> query={getProfile}> {({ data, loading, error }) => { if (loading) return "Loading …"; if (error) return "Loading …"; if (!data) return null; return <div>{data.user.name}</div>; }} </Query>;
<Query<ProfileData> query={getProfile}> {({ result }) => { switch (result.type) { case "loading": return "Loading …"; case "error": return "Sorry, something went wrong"; case "data": return <div>{result.data.user.name}</div>; default: return assertNever(result); } }} </Query>;
<GetUserQuery variables=userQuery##variables> ...{ ({result}) => switch (result) { | Loading => <div> "Loading"->s </div> | Error(error) => <div> error##message->s </div> | Data(response) => <div> response##user##name->s </div> } } </GetUserQuery>;
notifyOnNetworkStatusChange
Usage & Complexity Type your Code
The End @nikgraf

Type Systems & Props Design - Exploring PropTypes, TypeScript, Flow & Reason