今天我碰到个问题:假设有个键值对对象,键是某种状态,值是转换该状态的方法。
// 示例用的状态类型
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
}
原因就像演讲标题说的:“啥也不做” 也是一种操作。 你可能觉得这样多了没必要的函数调用,但为了代码更简洁,这个权衡值得。
这种模式的核心是:先明确 “默认行为”(也就是 “啥也不做”),再给它对应的处理逻辑。这样能减少多余的条件判断(条件判断最容易让代码变复杂),让代码更简单。