Skip to content

Latest commit

 

History

History
602 lines (438 loc) · 8.83 KB

deck.mdx

File metadata and controls

602 lines (438 loc) · 8.83 KB

import {Image, Appear} from 'mdx-deck' import {CodeSurfer, CodeSurferColumns, Step} from 'code-surfer' import {vsDark} from '@code-surfer/themes'

import References from './References' import Multiline from './Multiline' import Logos from './Logos' import Me from './Me'

import typescript from './images/typescript.png' import whatIf from './images/what-if.jpg'

export const theme = vsDark

<span style={{ display: 'none' /* The first h1 (or h2, h3, ...) is used as title but it has to be MD one */ }}>

Modeling your way to consistency

<Multiline Elem="h1" margin={50} lines={['Modeling your way', 'to consistency']} />


🎯 Goal 🎯

Making impossible states impossible


<Multiline lines={['Domain Modeling &', 'Type Driven Development']} />


<img src={typescript} style={{ width: '22vw' }} />


😡 🙅 👮 🤬


<img src={whatIf} style={{ height: '90vh' }} />


<Multiline lines={['🤓 Disclaimer 😬', 'There will be code!']} />


type AsyncData<T> = {
  isLoading: boolean
  didLoad: boolean
  data?: T
  error?: Error
}

const notAsked = {
  isLoading: false,
  didLoad: false,
  data: undefined,
  error: undefined,
}

const loading = {
  isLoading: true,
  didLoad: false,
  data: undefined,
  error: undefined,
}

const success = {
  isLoading: false,
  didLoad: true,
  data: user,
  error: undefined,
}

const failure = {
  isLoading: false,
  didLoad: true,
  data: undefined,
  error: error,
}

type AsyncData<T> = {
  isLoading: boolean
  didLoad: boolean
  data?: T
  error?: Error
}

const LoadingUser: FC<{ user: AsyncData<User> }> = ({ user }) => {
  if (user.isLoading) {
    return <Loading />
  }

  if (user.didLoad && user.error) {
    return <ErrorMessage error={user.error} />
  }

  if (user.didLoad && user.data) {
    return <UserCard user={user.data} />
  }

  return <Idle />
}
type AsyncData<T> = {
  isLoading: boolean
  didLoad: boolean
  data?: T
  error?: Error
}

const LoadingUser: FC<{ user: AsyncData<User> }> = ({ user }) => {
  if (user.isLoading) {
    return <Loading />
  }

  if (user.didLoad && user.error) {
    return <ErrorMessage error={user.error} />
  }

  if (user.didLoad && user.data) {
    return <UserCard user={user.data} />
  }

  return <Idle />
}

<Multiline lines={['🚢 📦', 'All good, right?']} />


const leWhat: AsyncData<User> = {
  isLoading: true,
  didLoad: true,
  data: user,
  error: someError,
}

type AsyncData<T> = {
  isLoading: boolean
  didLoad: boolean
  data?: T
  error?: Error
}

🤔 What to do? 🤔

PR reviews?

Unit tests?


🦄 Better Types 🦄


type Status =
  | "notAsked"
  | "loading"
  | "success"
  | "failure"

type AsyncData<T> = {
  status: Status
  data?: T
  error?: Error
}

type Status =
  | "notAsked"
  | "loading"
  | "success"
  | "failure"

type AsyncData<T> = {
  status: Status
  data?: T
  error?: Error
}

const LoadingUser: FC<{ user: AsyncData<User> }> = ({ user }) => {
  if (user.status === 'notAsked') {
    return <Idle />
  }

  if (user.status === 'loading') {
    return <Loading />
  }

  if (user.status === 'failure') {
    const error = user.error.message || 'Error!'
    return <ErrorMessage error={error} />
  }

  return user.data
    ? <UserCard user={user.data} />
    : null
}

<Multiline lines={['🚢 📦', 'Is it good now?']} />


const porQue: AsyncData<User> = {
  status: 'success',
  data: undefined,
  error: new Error('Something went wrong'),
}

Almost consistent 🤷


<Multiline lines={['Discriminated Unions', '😎 to the rescue 😎']} />


type AsyncData<T> =
  | {status: 'notAsked'}
  | {status: 'loading'}
  | {status: 'success', data: T}
  | {status: 'failure', error: Error}
type AsyncData<T> =
  | {status: 'notAsked'}
  | {status: 'loading'}
  | {status: 'success', data: T}
  | {status: 'failure', error: Error}

const nope: AsyncData<User> = {
  status: 'failure', 
}

const niet: AsyncData<User> = {
  status: 'success',
  error: new Error('Something wrong'),
}
type AsyncData<T> =
  | {status: 'notAsked'}
  | {status: 'loading'}
  | {status: 'success', data: T}
  | {status: 'failure', error: Error}

const nope: AsyncData<User> = {
  status: 'failure', 
}

const niet: AsyncData<User> = {
  status: 'success',
  error: new Error('Something wrong'),
}

type AsyncData<T> =
  | {status: 'notAsked'}
  | {status: 'loading'}
  | {status: 'success', data: T}
  | {status: 'failure', error: Error}

const LoadingUser: FC<{ user: AsyncData<User> }> = ({ user }) => {
  if (user.status === 'notAsked') {
    return <Idle />
  }

  if (user.status === 'loading') {
    return <Loading />
  }

  if (user.status === 'failure') {
    return <ErrorMessage error={user.error} />
  }

  return <UserCard user={user.data} />
}

<Multiline Elem="h1" lines={[ '🦄 🤗 🎉 🦺', '🚢 📦', ]} />


Conclusion 👀


<Multiline Elem="h3" lines={['Domain Modeling &', 'Type Driven Development', '💪 Enable us 👍']} />


<img src={typescript} style={{ width: '22vw' }} />


Bonus 🎁

📜 Let's make it declarative λ 🗺


const LoadingUser: FC<{ user: AsyncData<User> }> = ({ user }) => {
  if (props.status === 'notAsked') {
    return <Idle />
  }

  if (props.status === 'loading') {
    return <Loading />
  }

  if (props.status === 'failure') {
    return <ErrorMessage error={props.error} />
  }

  return <UserCard user={props.data} />
}
type AsyncData<T> =
  | {status: 'notAsked'}
  | {status: 'loading'}
  | {status: 'success', data: T}
  | {status: 'failure', error: Error}

type Matcher<T, R> = {
  notAsked: () => R
  loading: () => R
  success: (data: T) => R
  failure: (error: Error) => R
}
type AsyncData<T> =
  | {status: 'notAsked'}
  | {status: 'loading'}
  | {status: 'success', data: T}
  | {status: 'failure', error: Error}

type Matcher<T, R> = {
  notAsked: () => R
  loading: () => R
  success: (data: T) => R
  failure: (error: Error) => R
}

const LoadingUser: FC<{ user: AsyncData<User> }> = ({ user }) => {
  if (props.status === 'notAsked') {
    return <Idle />
  }

  if (props.status === 'loading') {
    return <Loading />
  }

  if (props.status === 'failure') {
    return <ErrorMessage error={props.error} />
  }

  return <UserCard user={props.data} />
}
type AsyncData<T> =
  | {status: 'notAsked'}
  | {status: 'loading'}
  | {status: 'success', data: T}
  | {status: 'failure', error: Error}

type Matcher<T, R> = {
  notAsked: () => R
  loading: () => R
  success: (data: T) => R
  failure: (error: Error) => R
}

const match = <T, R>(m: Matcher<T, R>, ad: AsyncData<T>): R => {
  if (ad.status === 'notAsked') {
    return m.notAsked()
  }

  if (ad.status === 'loading') {
    return m.loading()
  }

  if (ad.status === 'failure') {
    return m.failure(ad.error)
  }

  return m.success(ad.data)
}
type AsyncData<T> =
  | {status: 'notAsked'}
  | {status: 'loading'}
  | {status: 'success', data: T}
  | {status: 'failure', error: Error}

type Matcher<T, R> = {
  notAsked: () => R
  loading: () => R
  success: (data: T) => R
  failure: (error: Error) => R
}

const match = <T, R>(m: Matcher<T, R>, ad: AsyncData<T>) => {
  if (ad.status === 'notAsked') {
    return m.notAsked()
  }

  if (ad.status === 'loading') {
    return m.loading()
  }

  if (ad.status === 'failure') {
    return m.failure(ad.error)
  }

  return m.success(ad.data)
}

const LoadingUser: FC<{ user: AsyncData<User> }> = ({ user }) =>
  match<User, React.ReactNode>(
    {
      notAsked: () => <Idle />,
      loading: () => <Loading />,
      failure: (error) => <ErrorMessage error={error} />,
      success: (user) => <UserCard user={user} />,
    },
    user,
  )

📚 Resources 🤓


<Multiline lines={['🦄 Happy & safe coding! 🖖', 'Thanks!']} />