reactjs - 设置状态类型缩小
我有一个函数式组件,接收从 SSR(NextJS)传来的 props,并且内部有如下的状态:
const Component = ({err, n}: {err?: ErrorType, n?: N})=>{
const [x, setX] = useState(n || null)
...
if(err){
return <ErrorComponent err={err}>
}
else if(!(x===null)){
return <SomeComponent setX={setX}>
}
}
x 可以被看作是类型 N | null。然而,在“类型缩小” if 条件之后,虽然 x 的类型现在是 N,但 setX 的类型仍然是 Dispatch<SetState<N | null>>。然而,SomeComponent 期望 setX 的类型是 Dispatch<SetState<N>>。
我如何在不使用类型断言的情况下强制类型缩小?
我也尝试过这个,但 React 抛出了一个 React Hook "useState" 是有条件调用的。React Hooks 必须在每次组件渲染中以完全相同的顺序调用。 错误。
const Component = ({err, n}: {err?: ErrorType, n?: N})=>{
if(err || !n){
return <ErrorComponent err={err}>
}
const [x, setX] = useState(n)
return <SomeComponent setX={setX}>
}
答案#1
Typescript 按预期工作。它不会缩小类型,因为这样做不安全。
Dispatch<SetState<N>> 不能赋值给 Dispatch<SetState<N | null>>。让我们来详细解析一下。
Dispatch<SetState<N>> 等价于 ((action: N | ((prevValue: N) => N)) => void.
(action: N) => void 可以赋值给 (action: N | null) => void,但是 (action: (prevValue: N) => N) => void 不能赋值给 (action: (prevValue: N | null) => N | null) => void。
x 非空并不意味着 setX 会提供非空的 prevValue,因为 x 可能是过时的。在这方面,这两种类型是独立的。
如果你可以不用 set state 动作,你可以将 setX 属性类型设置为 (action: N) => void。这样它就可以赋值给 Dispatch<SetState<N | null>>。当然,缺点是 SomeComponent 不能用一个函数来调用 setX。
如果你需要在 SomeComponent 中使用函数设置状态的操作,你必须传入一个在调用操作回调之前执行运行时类型检查的函数,以使其真正类型安全。
以下对 setX 的扩展用一个无操作更新来替换类型不安全的调用。
const typeSafeSetX = (action: SetState<N | null>) => typeof action === 'function' ? setX(prevValue => prevValue === null ? prevValue : action(prevValue)) : setX(action);
我不建议你这样做,因为这只是用一种不良行为替代另一种不良行为。
