如何让打字稿推断间接函数参数类型?

时间:2021-04-29 12:43:52

标签: reactjs typescript typescript-typings immer.js

enter image description here

如何将 e 的类型推断为 React.ChangeEvent<HTMLInputElement>

完整代码:

import React, { useState, useCallback } from 'react';
import produce from 'immer';

type RestTuple<T extends [any, ...any[]]> = T extends [any, ...infer R] ? R : never;

function useImmer<S>(initialState: S | (() => S)) {
  const [s, ss] = useState(initialState);
  const immer = useCallback(<F extends (s: S, ...es: any[]) => any>(fn: F) => (...es: RestTuple<Parameters<F>>) =>
    produce(s, s => {
      fn(s as S, ...es);
    }),
    [s]
  );
  return [s, immer, ss] as const;
}

export function MyComponent() {
  const [user, set_user] = useImmer({ name: 'xialvjun', age: 30 });
  return (
    <div>
      <input type="text" value={user.name} onChange={set_user((u, e) => (u.name = e.target.value))} />
    </div>
  );
}

1 个答案:

答案 0 :(得分:1)

您的 RestTuple 条件类型阻止编译器根据上下文推断回调参数,因为它需要通过 RestTuple 的定义“向后”进行推断。

相反,我倾向于使您的函数仅在元组类型 T 中通用,如下所示:

function useImmer<S>(initialState: S | (() => S)) {
    const [s, ss] = useState(initialState);
    const immer = useCallback(<T extends any[]>(fn: (s: S, ...es: T) => void) =>
        (...es: T) =>
            produce(s, s => {
                fn(s as S, ...es);
            }),
        [s]
    );
    return [s, immer, ss] as const;
}

那么你的电话似乎表现得更好:

export function MyComponent() {
    const [user, set_user] = useImmer({ name: 'xialvjun', age: 30 });
    return (
        <div>
            <input type="text" value={user.name} onChange={
                set_user((u, e) => (u.name = e.target.value))} />
        </div>
    );
}

如果将鼠标悬停在 IDE 中,您会看到 e 现在根据需要推断为 React.ChangeEvent<HTMLInputElement>

Playground link to code