【问题标题】:React: TypeScript generics, auto switch parameter-type from function (function as props)React:TypeScript 泛型,从函数自动切换参数类型(函数作为道具)
【发布时间】:2021-04-28 02:12:44
【问题描述】:

我想实现以下(在 React/TypeScript (.tsx) 中)

Main.tsx(编辑:botCommands 是一个数组,其中可以包含多个对象(commandbotResponse()

function Main() {
  return (
    <TestComponent
      botVariables={{
        tasks: [],
        groups: []
      }}
      botCommands={[
        {
          command: "-help",
          botResponse(botVariables) {
            return botVariables.tasks[0]
          }
        }
      ]}
    />
  );
}

TestComponent.tsx

interface BotCommand {
  command: string;
  botResponse(botVariables: any): string;
}

interface TestComponentProps {
  botVariables: any;
  botCommands: BotCommand[];
}

export default function TestComponent(props: TestComponentProps) {
  return (
    // magic
  );
}

Main.tsx 我希望在函数botResponse() 中自动调整类型(当前为any)。这样botVariables 的内容就会自动作为类型。

简单地说,如果我在botResponse() 函数“botVariables”中写入Main.tsx。 IDE 可以向我建议 tasksgroupsbotVariables 中的数据可以更改,因此您不能只在 TestComponent.tsx 文件中创建接口。

我尝试过使用 typescript 泛型,但不幸的是失败了。感谢您的帮助!

【问题讨论】:

  • botVariables的约束是什么?它总是具有属性tasksgroups 的对象,它们是数组吗? tasks 数组的内容是否不同,或者它们总是string

标签: javascript reactjs typescript typescript-generics


【解决方案1】:

如果我正确理解您的要求,botVariables 可以是任何东西,但我们提供给TestComponentbotVariablesbotCommands 需要相互匹配。即botCommandsbotResponse函数的参数与botVariables属性的类型相同。

为此,我们将BotCommand 接口设为泛型,其中T 类型代表botResponse 变量的类型。

interface BotCommand<T> {
  command: string;
  botResponse(botVariables: T): string;
}

我们的TestComponentProps 也是通用的,我们将T 应用于botVariablesbotCommands

interface TestComponentProps<T> {
  botVariables: T;
  botCommands: BotCommand<T>[];
}

这意味着组件也是通用的。

export default function TestComponent<T>(props: TestComponentProps<T>) {...}

从传递的变量创建响应没有问题,因为两个props 共享相同的T

export default function TestComponent<T>({ botVariables, botCommands }: TestComponentProps<T>) {
    return (
        <div>
            <h1>Bot Output</h1>
            {botCommands.map(command => (
                <div>
                    <div>Bot recieved command {command.command}</div>
                    <div>Bot responded {command.botResponse(botVariables)}</div>
                </div>
            ))}
        </div>
    );
}

当您使用TestComponent 时,您不需要显式声明泛型T,因为它可以从botVariables 属性中推断出来。如果botCommands 不匹配,打字稿会报错。

在这个例子中:

function Main() {
  return (
    <TestComponent
      botVariables={{
        tasks: ["first"],
        groups: ["something"]
      }}
      botCommands={[{
        command: "-help",
        botResponse(botVariables) {
          return botVariables.tasks[0]
        }
      }]}
    />
  );
}

botResponse 函数的推断签名是完整且正确的:

(method) BotCommand<{ tasks: string[]; groups: string[]; }>.botResponse(botVariables: {
    tasks: string[];
    groups: string[];
}): string

为了得到正确的推断,我不得不使用["first"] 而不是空数组,因为空数组的类型为never[],但我们想要string[]。可以使用空数组,但必须显式设置它的类型。

Playground Link

【讨论】:

    【解决方案2】:

    欢迎来到 StackOverflow 社区。​​p>

    Typescript 无法自动检测参数的类型。

    不管怎样,你可以选择使用Union Types或者Generic Types

    如果变量是一组有限的类型,您可以使用联合类型。

    interface MyObject {
        tasks: string[]
    }
    
    interface BotCommand {
      command: string;
      botResponse: (botVariables: string | number | MyObject) => string;
    }
    

    泛型示例

    interface BotCommand {
      command: string;
      botResponse: <T extends unknown>(botVariables: T) => string;
    }
    

    在这两种情况下,您都需要在使用之前“识别”对象的当前类型。为此,您可以使用Typeguard

    function isMyObject(x: unknown): x is MyObject {
      return (x as MyObject).tasks !== undefined
    }
    

    更多信息请查看playground

    【讨论】:

    • 如果我们仅处理BotCommand 界面,您所说的一切都是正确的,但您有点错过了更大的图景。在 TestComponent 组件的上下文中,botResponse 的参数不再是 unknown,因为它们与 botVariables 属性的类型相同。
    • 并不总是指定参数tasks:[]Linda Paiste 的回答帮助了我。感谢您的帮助!
    猜你喜欢
    • 1970-01-01
    • 2010-10-04
    • 2019-03-05
    • 2013-02-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多