是否可以为事件处理程序推断 useCallback 的类型签名?

dan*_*nvk 6 typescript reactjs material-ui

我在 React 中使用 TypeScript,并使用箭头函数作为Material UI<Select>组件的回调:

import React from 'react';
import MenuItem from '@material-ui/core/MenuItem';
import Select from '@material-ui/core/Select';

interface Props {
  value: string;
  onSelect: (value: string) => void;
}

function MySelector(props: Props) {
  return (
    <Select
      value={props.value}
      onChange={e => props.onSelect(e.value as string)}
    >
      <MenuItem value="a">A</MenuItem>
      <MenuItem value="b">B</MenuItem>
      <MenuItem value="c">C</MenuItem>
    </Select>
  )
}
Run Code Online (Sandbox Code Playgroud)

为了避免每次MySelector渲染时都传递一个新函数,我想使用useCallbackhook。虽然直接重构在运行时有效,但由于any事件参数上的隐式错误,它无法进行类型检查e

function MySelector(props: Props) {
  const handleChange = React.useCallback(
    e => props.onSelect(e.value as string),
 // ~ e implicitly has an any type
    [props.onSelect]
  );
  return (
    <Select
      value={props.value}
      onChange={handleChange}
    >
      { /* ... menu items ... */ }
    </Select>
  )
}
Run Code Online (Sandbox Code Playgroud)

将鼠标悬停e在原始符号上显示其类型为

React.ChangeEvent<{
    name?: string | undefined;
    value: unknown;
}>
Run Code Online (Sandbox Code Playgroud)

这是一口,但要修复我必须输入的错误:

const handleChange = React.useCallback(
  (
    e: React.ChangeEvent<{
      name?: string | undefined;
      value: unknown;
    }>,
  ) => props.onSelect(e.value as string),
  [props.onSelect],
);
Run Code Online (Sandbox Code Playgroud)

我可以吃蛋糕也吃吗?我可以使用useCallback钩子并仍然从上下文中获取事件参数的类型吗?

Lau*_*ent 0

以下是您如何通过常规反应来做到这一点。我一步步描述了每个操作,以便您可以尝试重现它们:

import React, { useCallback } from "react";
import "./App.css";

// First we get the DOM Attributes for the Element we care about:
// => `React.DOMAttributes<HTMLSelectElement>`
// Then we access the field we need
// => `...["onChange"]`
// Then we remove the "undefined" union
// => `NonNullable<...>`
// which gives you:
type OnChangeCallback = NonNullable<
  React.DOMAttributes<HTMLSelectElement>["onChange"]
>;

const App: React.FC = () => {
  // we use this type in the `useCallback` definition directly:
  const onChange = useCallback<OnChangeCallback>((e) => {
    console.log(e);
  }, []);

  return <select className="main" onChange={onChange}></select>;
};

export default App;
Run Code Online (Sandbox Code Playgroud)

它应该很容易适应您的组件库。

变成通用的,因此更容易导入和使用:

export type On<T, V extends keyof React.DOMAttributes<T>> = NonNullable<
  React.DOMAttributes<T>[V]
>;

// Use it with:
const myCallback: On<HTMLDivElement, "onMouseMove"> = (e) => {};
Run Code Online (Sandbox Code Playgroud)