如何为组件定义相互依赖的类型Props

时间:2019-10-29 13:09:06

标签: reactjs typescript

我试图对我的React-Typescript项目进行一些简化,但是我陷入了如何正确注释类型的困境。这是我正在使用的代码的精简示例:

https://codesandbox.io/s/working-example-qc5cq

// WORKING EXAMPLE
import * as React from "react";
import { render } from "react-dom";

const App: React.FC = () => {
  const handleClick = (value: string | number): void => {
    if (typeof value === "string") {
      // Do some string logic...
      console.log(`String: ${value}`);
    } else if (typeof value === "number") {
      // Do some number logic
      console.log(`Number: ${value}`);
    }
  };
  return (
    <div>
      <Button handleClick={handleClick} value="Hello" />
      <Button handleClick={handleClick} value={42} />
    </div>
  );
};

interface Props {
  handleClick: (value: string | number) => void;
  value: string | number;
}
const Button: React.FC<Props> = ({ handleClick, value }) => (
  <button onClick={(): void => handleClick(value)}>{value}</button>
);

const rootElement = document.getElementById("root");
render(<App />, rootElement);

我想要做的是将handleClick-函数拆分为两个单独的函数;一个用于处理string,一个用于处理number。当我尝试为Props组件键入Button时出现问题。这是我目前的位置:

https://codesandbox.io/s/wip-example-fied2

// WIP
import * as React from "react";
import { render } from "react-dom";

const App: React.FC = () => {
  const handleStringClick = (string: string) => {
    // Do some string logic...
    console.log(`String: ${string}`);
  };

  const handleNumberClick = (number: number): void => {
    // Do some number logic
    console.log(`Number: ${number}`);
  };

  return (
    <div>
      <Button handleClick={handleStringClick} value="Hello" />
      <Button handleClick={handleNumberClick} value={42} />
    </div>
  );
};

interface StringProps {
  handleClick: (string: string) => void;
  value: string;
}
interface NumberProps {
  handleClick: (number: number) => void;
  value: number;
}
type Props = StringProps | NumberProps;
const Button: React.FC<Props> = ({ handleClick, value }) => (
  <button onClick={(): void => ?handleClick(value)}>{value}</button>
);

const rootElement = document.getElementById("root");
render(<App />, rootElement);

我收到的错误是在handleClick(value)上:

  

类型'ReactText'的参数不能分配给类型的参数   '决不'。不能将“字符串”类型分配给“从不”类型。ts(2345)

这是可以理解的,因为value的类型被解释为string | number(实际上是ReactText,但这是React的简写),没有handleClick的版本可以处理这种情况。


问题是,我可以以某种方式注释Props以便handleClick取决于value的类型吗?换句话说,如果我将value设置为number,我可以使Typescript理解并强迫我传递handleClick(number)函数吗?如果我的思维定势或要达到的目标有点缺陷,请随时向我指出更好的方向。

2 个答案:

答案 0 :(得分:0)

一种实现此目的的方法是使Button通用:

type Props<T extends string | number> = {
  handleClick: (argument: T) => void;
  value: T;
}
const Button= <T extends string | number,>({ handleClick, value }: Props<T>) => (
  <button onClick={(): void => handleClick(value)}>{value}</button>
);
<Button handleClick={arg => { /** `arg` is of type `42` */}} value={42} />

答案 1 :(得分:0)

感谢答案Karol!我能够根据您的建议调整代码;我照原样离开了Props,然后扩展了Button代码,使其具有返回类型:

const Button = <T extends string | number>({
  handleClick,
  value
}: Props<T>): JSX.Element => (
  <button onClick={(): void => handleClick(value)}>{value}</button>
);

还找到了一个很好的消息来源:Passing Generics to JSX Elements in TypeScript