译:无即是有

8 阅读1分钟

今天我碰到个问题:假设有个键值对对象,键是某种状态,值是转换该状态的方法。

// 示例用的状态类型

type State = {
  count: number
  createdAt: Date
  isActive: boolean
  name: string
}

// 转换函数类型:输入什么返回什么
type Transform = <T>(x: T) => T

// 状态转换规则:不是每个状态都有转换
const STATE_TRANSFORMS: Partial<Record<keyof State, Transform>> = {
  count: x => x * 2, // count 翻倍
  name: x => x.toUpperCase(), // name 转大写
}

要是写个函数执行这些转换,有人可能会这么写:

function transformStates(state: State) {
  const nextState: State = {}
  for (const key of state) {
    const transform = STATE_TRANSFORMS[key]
    
    // 最后才判断,导致状态处理有两种逻辑
    if (transform) {
      nextState[key] = transform(state[key])
    } else {
      nextState[key] = state[key]
    }
  }
  return nextState
}

但我更愿意这么写:

// 恒等函数:输入啥返啥(就是“啥也不做”)

const identity = x => x

function transformStates(state) {
  const nextState = {}
  for (const key of state) {
    // 提前判断,把transform类型简化成只有Transform
    const transform = STATE_TRANSFORMS[key] ?? identity
    // 统一执行转换(“啥也不做”也是一种转换)
    nextState[key] = transform(state[key])
  }
  return nextState
}

原因就像演讲标题说的:“啥也不做” 也是一种操作。 你可能觉得这样多了没必要的函数调用,但为了代码更简洁,这个权衡值得。

这种模式的核心是:先明确 “默认行为”(也就是 “啥也不做”),再给它对应的处理逻辑。这样能减少多余的条件判断(条件判断最容易让代码变复杂),让代码更简单。

英文原文