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

How to let typescript infer the indirect function parameter type?

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>
  );
}

您的 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