我有一个状态更新器函数,它接收以前的状态,并允许您通过整个对象或函数更新器来设置新状态。例如:
const prevState = { x: 3 };
// Option A
const nextState = updateState(prevState, { x: 4 });
// Option B
const nextState = updateState(prevState, prevState => ({ x: prevState.x + 1 }));
我可以在Flow中输入它,但不能再让它在TypeScript中工作。下面是函数:
function updateState<State>(
prevState: State,
updater: State | ((prevState: State) => State),
): State {
return typeof updater === 'function' ? updater(prevState) : updater;
}
这就是错误:
无法调用缺少调用签名的类型的表达式。类型'((prevState: State) => State) | (State & Function)‘没有兼容的调用签名。
(State & Function)
是从哪里来的?
我的理解是State
本身也可以是一个函数,因为State
是无类型的。但也许我可以将State
限制为非函数类型...
有没有办法在TypeScript中做到这一点?
发布于 2018-12-04 17:34:59
问题在于State
是一个泛型类型参数,所以没有理由State
不能是一个函数。因此,当您使用类型保护时,缩小范围的结果不能将State
排除在类型之外。类型保护可以说的是,新类型也将是一个函数,这意味着原始类型与Function
相交,遵循这些类型如何扩展的规则,我们得到:(State | ((prevState: State) => State)) & Function = (State & Function) | (((prevState: State) => State) & Function ) = (State & Function) | (((prevState: State) => State))
我们可以做的一件事是确保State
与Function不兼容(没有明确的方法来说明泛型类型不能是特定类型,我们只能说类型可以是什么)。
function updateState<State extends { call?: never } & Record<string, any>>( // if State has a call member it must be never
prevState: State,
updater: State | ((o: State) => State),
): State {
return typeof updater === "function" ? updater(prevState) : updater;
}
该解决方案的一个缺点是,约束基本上强制State
成为对象。如果你也想让State
成为一个原语,你需要用一个联合来添加原语:
function foo<S extends string | number | boolean | null | undefined | { call?: never }>(initial: S | (() => S)): S {
return typeof initial === 'function' ? initial() : initial;
}
foo('test');
https://stackoverflow.com/questions/53609286
复制相似问题