在一个 TypeScript 函数中,基于第二个可选参数来输入第一个参数。

huangapple go评论77阅读模式
英文:

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&lt;State&gt; = &lt;Selected = State&gt;(
    update: Selected | (() =&gt; Selected) | (() =&gt; Promise&lt;Selected&gt;),
    selector?: (state: State) =&gt; Selected
) =&gt; Promise&lt;Selected&gt;;

const initialState = {
    a: {
        b: {
            c: 0,
        },
    },
};

const setState: NestedStateSetter&lt;typeof initialState&gt; = (update, selector) =&gt; {
    throw &quot;Yeet&quot;
};

setState(1, (state) =&gt; state.a.b.c) // Ok
setState(() =&gt; 2, (state) =&gt; state.a.b.c) // Ok
setState(async () =&gt; 5, (state) =&gt; state.a.b.c) // Not ok

But the third call to 'setState' is giving us this error on the (state) =&gt; 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 | (() =&gt; Selected) | (() =&gt; Promise&lt;Selected&gt;)

Use:

update: (() =&gt; Promise&lt;Selected&gt;) | (() =&gt; Selected) | Selected

Complete snippet:

type NestedStateSetter&lt;State&gt; = &lt;Selected = State&gt;(
  update: (() =&gt; Promise&lt;Selected&gt;) | (() =&gt; Selected) | Selected,
  selector?: (state: State) =&gt; Selected
) =&gt; Promise&lt;Selected&gt;;

const initialState = {
  a: {
    b: {
      c: 0,
    },
  },
};

const setState: NestedStateSetter&lt;typeof initialState&gt; = (update, selector) =&gt; {
  throw &#39;Yeet&#39;;
};

setState(1, (state) =&gt; state.a.b.c); // Ok
setState(
  () =&gt; 2,
  (state) =&gt; state.a.b.c
); // Ok
setState(
  async () =&gt; 5,
  (state) =&gt; state.a.b.c
); // Ok

<sup>Playground link</sup>

huangapple
  • 本文由 发表于 2023年3月7日 15:40:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/75659140.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定