英文:
Typing first parameter in a typescript function based on the second optional parameter
问题
以下是代码的中文翻译部分:
我正在为React开发一个状态管理库,我想要添加一个帮助函数,使用户可以使用选择器轻松地从React外部设置深度嵌套的状态。到目前为止,帮助函数看起来是这样的:
type NestedStateSetter<State> = <Selected = State>(
update: Selected | (() => Selected) | (() => Promise<Selected>),
selector?: (state: State) => Selected
) => Promise<Selected>;
const initialState = {
a: {
b: {
c: 0,
},
},
};
const setState: NestedStateSetter<typeof initialState> = (update, selector) => {
throw "Yeet";
};
setState(1, (state) => state.a.b.c) // Ok
setState(() => 2, (state) => state.a.b.c) // Ok
setState(async () => 5, (state) => state.a.b.c) // Not ok
但是对于上面第三次调用setState
,在第三个调用中的(state) => state.a.b.c
部分会导致以下错误:
类型“(state: { a: { b: { c: number; }; }; }) => number”的参数不能赋给类型“(state: { a: { b: { c: number; }; }; }) => Promise
”的参数。
类型“number”不能赋给类型“Promise”。
TypeScript Playground链接
Stackblitz链接
我尝试使用柯里化函数,但这将是一个破坏性的更改,我尽量避免这种情况。
英文:
I'm working on a state manager library for React, I wanted to add a helper function which would allow users to set deeply nested states easily from outside of React with the use of a selector and so far the helper function looks something like this:
type NestedStateSetter<State> = <Selected = State>(
update: Selected | (() => Selected) | (() => Promise<Selected>),
selector?: (state: State) => Selected
) => Promise<Selected>;
const initialState = {
a: {
b: {
c: 0,
},
},
};
const setState: NestedStateSetter<typeof initialState> = (update, selector) => {
throw "Yeet"
};
setState(1, (state) => state.a.b.c) // Ok
setState(() => 2, (state) => state.a.b.c) // Ok
setState(async () => 5, (state) => state.a.b.c) // Not ok
But the third call to 'setState' is giving us this error on the (state) => state.a.b.c
of the third call above:
> Argument of type '(state: { a: { b: { c: number; }; }; }) => number' is not assignable to parameter of type '(state: { a: { b: { c: number; }; }; }) => Promise<number>'.
Type 'number' is not assignable to type 'Promise<number>'.(2345)
TypeScript Playground link
<br>Stackblitz link
I tried to use a curried function but that would be a breaking change and I would rather avoid it as much as possible.
答案1
得分: 2
你需要按最具体到最不具体的顺序对联合类型进行排序,以防止 TypeScript 匹配到第一个、最通用的类型。
所以,不要使用:
update: Selected | (() => Selected) | (() => Promise<Selected>)
而要使用:
update: (() => Promise<Selected>) | (() => Selected) | Selected
英文:
You need to order the types in your union from most specific to least specific to prevent TypeScript from matching the first, most generic type.
So, instead of:
update: Selected | (() => Selected) | (() => Promise<Selected>)
Use:
update: (() => Promise<Selected>) | (() => Selected) | Selected
Complete snippet:
type NestedStateSetter<State> = <Selected = State>(
update: (() => Promise<Selected>) | (() => Selected) | Selected,
selector?: (state: State) => Selected
) => Promise<Selected>;
const initialState = {
a: {
b: {
c: 0,
},
},
};
const setState: NestedStateSetter<typeof initialState> = (update, selector) => {
throw 'Yeet';
};
setState(1, (state) => state.a.b.c); // Ok
setState(
() => 2,
(state) => state.a.b.c
); // Ok
setState(
async () => 5,
(state) => state.a.b.c
); // Ok
<sup>Playground link</sup>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论