a blog by Boris Cherny

React+TypeScript: Use unions of objects for props

When typing React component props using TypeScript, I often see code that makes use of optional and nullable fields:

type Props = {
  href?: string
  onClick?: () => void
  text: string

function Button(props: Props) {
  if ('href' in props) {
    return <a className="Button" href={props.href}>{props.text}</a>
  if ('onClick' in props) {
    return <button className="Button" onClick={props.onClick}>
  throw ReferenceError(
    'You must pass either href or onClick to <Button />'

let a = <Button text="Click me" href="" />
let b = <Button text="Click me" onClick={() => {}} />

// OK. Should be an error
let c = <Button
  text="Click me"
  onClick={() => {}} />

// OK. Throws an error at runtime
let d = <Button text="Click me" />

In this example, Button is a React component that takes a prop text, and either an href or onClick prop controlling what happens when you press the button. Either href or onClick might be defined, and we throw a runtime exception if neither of them are defined.


On Contagion

If you have a tree with a node that has a property P, and all of its parents also need to have property P, then P is contagious.

When can that happen?


On Derived State

Derived state for frontend applications comes up a lot. State B depends on State A, so after you update A, by the time you read B you want it to be updated based on the latest value of A. There’s a bunch of ways to model this, involving different tradeoffs:

  • Do you treat derived data and non-derived data the same?
  • Should users interact with the two the same way?
  • Do you re-compute dependent data in a lazy or eager way?
  • How much control do you need over re-computing derived data?

A popular solution is selector libraries like Reselect. The way they work is: