Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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

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

Badly designed props can lead to components that are frustrating to use. While there are a couple patterns to make components more pleasant to use, ultimately it needs a type system in combination with a good editor integration for an even better developer experience. Yet not all type systems are the same. They offer different features and therefore lead to different experiences.

Nikolaus Graf

November 30, 2018
Tweet

More Decks by Nikolaus Graf

Other Decks in Programming

Transcript

  1. Type Systems &
    Props Design

    Exploring PropTypes, TypeScript, Flow & Reason

    View Slide

  2. Let’s start without types

    View Slide


  3. View Slide


  4. View Slide


  5. View Slide


  6. View Slide


  7. View Slide


  8. View Slide


  9. View Slide


  10. View Slide


  11. View Slide


  12. View Slide


  13. View Slide

  14. View Slide

  15. A or B or C

    View Slide

  16. easy to fix … is it though?

    View Slide

  17. Type systems to the rescue!

    View Slide


  18. PropTypes.string.isRequired;

    View Slide

  19. PropTypes.string.isRequired;

    View Slide

  20. PropTypes.oneOf(["down", "up"]).isRequired;

    View Slide

  21. View Slide

  22. View Slide

  23. export enum Direction {
    Up = "up",
    Down = "down"
    }
    interface Props {
    direction: Direction;
    }
    import { Direction } from "./Arrow";

    View Slide

  24. interface Props {
    direction: "up" | "down";
    }

    View Slide

  25. View Slide

  26. View Slide

  27. View Slide

  28. interface Props {
    direction: "up" | "down";
    }

    View Slide

  29. View Slide

  30. View Slide


  31. type direction = Down | Up;

    View Slide

  32. View Slide

  33. A{x} or B{x, y} or C

    View Slide

  34. Draft{content}

    Post{content, publishedAt}

    View Slide

  35. View Slide

  36. interface DraftPost {
    status: "draft";
    content: String;
    }
    interface PublishedPost {
    status: "published";
    content: String;
    publishedAt: Date;
    }
    type Props = DraftPost | PublishedPost;

    View Slide

  37. export default function Post(props: Props) {
    switch (props.status) {
    case "draft":
    return {props.content};
    case "published":
    return {props.content}{props.publishedAt};
    }
    }

    View Slide

  38. View Slide

  39. Discriminated Unions

    View Slide

  40. Draft{status, content}

    Post{status, content, publishedAt}

    View Slide

  41. interface DraftPost {
    status: "draft";
    content: String;
    }
    interface PublishedPost {
    status: "published";
    content: String;
    publishedAt: Date;
    }
    type Post = DraftPost | PublishedPost;
    interface Props {
    post: Post;
    }

    View Slide

  42. post={{
    status: "published",
    content: "Unicorns & Cats",
    publishedAt: new Date()
    }}
    />

    View Slide

  43. View Slide

  44. type DraftPost = {
    status: "draft",
    content: string
    };
    type PublishedPost = {
    status: "published",
    content: string,
    publishedAt: Date
    };
    type Props = DraftPost | PublishedPost;

    View Slide

  45. export default function Post(props: Props) {
    switch (props.status) {
    case "draft":
    return {props.content};
    case "published":
    return {props.content}{props.publishedAt};
    default:
    return null;
    }
    }

    View Slide

  46. View Slide

  47. type DraftPost = {|
    status: "draft",
    content: string
    |};
    type PublishedPost = {|
    status: "published",
    content: string,
    publishedAt: Date
    |};
    type Props = DraftPost | PublishedPost;

    View Slide

  48. View Slide

  49. Disjoint Unions

    View Slide

  50. Draft{status, content}

    Post{status, content, publishedAt}

    View Slide

  51. post={{
    status: "published",
    content: "Unicorns & Cats",
    publishedAt: new Date()
    }}
    />

    View Slide

  52. View Slide

  53. type post =
    | Draft(string)
    | Published(string, int);

    View Slide

  54. let component = ReasonReact.statelessComponent("Arrow");
    let make = (~post: post, _children) => {
    ...component,
    render: _self =>
    switch (post) {
    | Draft(content) => content->s
    | Published(content, timestamp) =>
    content->s timestamp->string_of_int->s
    },
    };

    View Slide

  55. View Slide

  56. Variants

    View Slide

  57. Draft(content)

    Post(content, publishedAt)

    View Slide

  58. Render Props

    View Slide

  59. query={getProfile}>
    {({ data, loading, error }) => {
    if (loading) return "Loading …";
    if (error) return "Loading …";
    if (!data) return null;
    return {data.user.name};
    }}
    ;

    View Slide

  60. query={getProfile}>
    {({ result }) => {
    switch (result.type) {
    case "loading":
    return "Loading …";
    case "error":
    return "Sorry, something went wrong";
    case "data":
    return {result.data.user.name};
    default:
    return assertNever(result);
    }
    }}
    ;

    View Slide

  61. View Slide


  62. ...{
    ({result}) =>
    switch (result) {
    | Loading => "Loading"->s
    | Error(error) => error##message->s
    | Data(response) => response##user##name->s
    }
    }
    ;

    View Slide

  63. View Slide

  64. notifyOnNetworkStatusChange

    View Slide

  65. Usage & Complexity
    Type your Code

    View Slide

  66. The End
    @nikgraf

    View Slide