【问题标题】:React/TypeScript - Extending component with generic type and reuse generic type within componentReact/TypeScript - 使用泛型类型扩展组件并在组件内重用泛型类型
【发布时间】:2021-02-28 16:42:13
【问题描述】:

我有以下组件,它采用通用 nodepatchCurrentNode 道具。

import { css } from '@emotion/react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import React, {
  PropsWithChildren,
  useState,
} from 'react';
import BaseNodeData from '../../types/BaseNodeData';
import { PatchCurrentNode } from '../../types/BaseNodeProps';

type Props<NodeData extends BaseNodeData = BaseNodeData> = {
  node: NodeData;
  patchCurrentNode: PatchCurrentNode<NodeData>;
};

/**
 * Input that stores the name of the node's selected value.
 *
 * Some nodes ask for an input (text, choice, etc.), the selected value will be stored and be indexed using the selected variable's name.
 */
export const VariableNameInput: <NodeData extends BaseNodeData = BaseNodeData>(p: PropsWithChildren<Props<NodeData>>) => React.ReactElement = (props) => {
  const {
    node,
    patchCurrentNode,
  } = props;
  const {
    width = 200,

  } = node;
  console.log('node', node);
  const [variableName, setVariableName] = useState<string | undefined>(node?.data?.variableName);
  console.log('VariableNameInput variableName', variableName);

  const onChange = (event: any) => {
    console.log('onChange variableName', event, event?.target?.value);
    setVariableName(event?.target?.value);
  };

  const onSubmit = () => {
    console.log('onSubmit variableName', variableName);
    patchCurrentNode({
      data: {
        variableName: variableName,
      },
    });
  };

  return (
    <div
      className={'variable-name-container'}
      css={css`
        position: absolute;
        bottom: 0;
        background-color: black;
        width: ${width}px;
        height: 50px;
        margin-left: -15px;
        margin-bottom: -15px;
        padding-left: 15px;
        border-radius: 5px;

        .variable-name {
          width: ${width - 50}px;
          margin-top: 12px;
          margin-left: -5px;
          padding-left: 5px;
          background-color: black;
          color: ${variableName?.length ? 'white' : '#6E6E6E'}; // Change different color between placeholder and actual value
          border: 1px solid #6E6E6E;
        }

        .submit {
          color: #6E6E6E;
          margin-left: 10px;
          cursor: pointer;
        }
      `}
    >
      <input
        className={'variable-name'}
        placeholder={'Variable name'}
        value={variableName}
        onChange={onChange}
      />

      <FontAwesomeIcon
        className={'submit'}
        icon={['fas', 'paper-plane']}
        onClick={onSubmit}
      />
    </div>
  );
};

export default VariableNameInput;

它是这样使用的:

有以下几种:

import BaseNodeData from '../BaseNodeData';
import { QuestionNodeAdditionalData } from './QuestionNodeAdditionalData';

export type QuestionNodeData = BaseNodeData<QuestionNodeAdditionalData>;

---

import BaseNodeAdditionalData from '../BaseNodeAdditionalData';
import { QuestionChoiceType } from './QuestionChoiceType';

export type QuestionNodeAdditionalData = BaseNodeAdditionalData & {
  text?: string;
  questionType?: QuestionChoiceType;
  variableName?: string;
};

组件中泛型的实现有效。但是,我仍然有 TS 错误:


如何在组件的实现中重用通用 NodeData 类型?

我在想我可以做类似node?.data?.variableName as NodeData 的事情,但那里不知道NodeData


编辑Solution

【问题讨论】:

    标签: reactjs typescript generics


    【解决方案1】:

    由于您将组件的类型签名与实现分开定义,因此您无法访问组件主体内部的通用 NodeData

    export const VariableNameInput: <NodeData extends BaseNodeData = BaseNodeData>(p: PropsWithChildren<Props<NodeData>>) => React.ReactElement = (props) => {
    

    不要将类型注释应用于VariableNameInput 变量,而是将它们直接应用于函数参数和返回类型。

    export const VariableNameInput = <NodeData extends BaseNodeData = BaseNodeData>(props: PropsWithChildren<Props<NodeData>>): React.ReactElement => {
    

    您现在可以在组件内访问NodeData

    useState<string | undefined>((node as NodeData).data?.variableName);
    

    但是你不会通过断言这个得到任何额外的信息,因为node 是一个必需的属性并且它总是类型NodeData

    您似乎更有可能需要将variableName 定义为BaseNodeAdditionalData 类型上的可选string 属性。如果您编辑帖子以包含 BaseNodeDataPatchCurrentNode 的类型(及其依赖项),那么也许我可以帮助您解决根本问题。

    回调patchCurrentNode 会给你带来最大的麻烦,因为NodeData 可以通过需要额外的属性或更具体的值来扩展BaseNodeData。你可以断言as Partial&lt;NodeData&gt;,但你要确信你所做的任何断言都是正确的。

    Typescript Playground Link

    【讨论】:

    • 感谢您的解释,您的分析是正确的。我不确定为函数本身或直接为props 属性编写 TS 类型是否是“最好的”。我通常在函数上执行此操作,但在这种情况下似乎不起作用。这是我通过玩了一下想出的:export const VariableNameInput: React.FunctionComponent&lt;PropsWithChildren&lt;Props&gt;&gt; = &lt;NodeData extends BaseNodeData = BaseNodeData&gt;(props: PropsWithChildren&lt;Props&lt;NodeData&gt;&gt;): React.ReactElement =&gt; {}。但是React.FunctionComponent&lt;PropsWithChildren&lt;Props&gt;&gt; 感觉是重复的和不必要的。
    • 其中一些只是偏好,但我个人不会将任何类型应用于组件本身。如果输入了函数参数和返回值,则没有必要。 React.FunctionComponent 不适用于通用组件,因此只需键入函数本身即可。
    • 不幸的是,我无法在BaseNodeData 中定义variableName,因为BaseNodeData 是一个通用的“NodeData”类型,它正在被专门的类型增强。我想在这种情况下,我唯一的解决方案是创建一个新的NodeDataWithVariableName(或类似的)并在VariableNameInput 组件中使用它而不是BaseNodeData
    • 我认为那会很好。如果您的组件希望您有一个具有 variableName 属性的节点类型,那么您应该从您的道具中要求它。
    • 感谢琳达的帮助,我找到了解决方案。我敢打赌它可以改进/简化,但现在就可以了! github.com/Vadorequest/poc-nextjs-reaflow/commit/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-12
    • 1970-01-01
    • 1970-01-01
    • 2022-11-18
    • 2022-12-03
    相关资源
    最近更新 更多