React/TypeScript:泛型组件扩展及组件内部泛型类型复用问题
解决React泛型组件内部无法访问泛型类型的问题
你遇到的核心问题是泛型参数的作用域限制:之前把组件的泛型参数定义在了函数的类型注解上,而非函数本身的声明中,导致函数体内部无法直接引用NodeData这个泛型类型。
核心解决方案:调整泛型组件的定义方式
把泛型参数从类型注解部分移到函数声明的尖括号里,这样在组件内部就能直接访问NodeData类型了。下面是关键修改点:
// 原来的写法(泛型仅存在于类型注解,内部无法访问NodeData) export const VariableNameInput: <NodeData extends BaseNodeData = BaseNodeData>(p: PropsWithChildren<Props<NodeData>>) => React.ReactElement = (props) => { ... } // 修改后的写法(泛型属于函数本身,内部可直接调用NodeData) export const VariableNameInput = <NodeData extends BaseNodeData = BaseNodeData>( props: PropsWithChildren<Props<NodeData>> ): React.ReactElement => { // 这里可以直接使用NodeData类型了! const { node, patchCurrentNode } = props; // ... 其余逻辑 };
为什么原来的写法不生效?
当泛型参数写在:后面的类型注解里时,它只属于函数的类型签名,仅在类型检查阶段生效,函数体内部的代码无法识别这个泛型名称。而把泛型移到函数声明的尖括号后,它就成为了函数本身的一部分,作用域覆盖整个函数体,自然可以直接引用。
额外的类型安全优化
我还针对你的代码做了几个小优化,提升类型安全性:
- 把
onChange的event类型从any改为React.ChangeEvent<HTMLInputElement>,避免任意类型带来的潜在错误; - 移除
useState的手动类型指定,让TypeScript自动从node?.data?.variableName推导类型; - 给
input的value添加|| '',避免undefined触发React受控组件警告。
修改后的完整组件代码
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>( props: PropsWithChildren<Props<NodeData>> ): React.ReactElement => { const { node, patchCurrentNode } = props; const { width = 200 } = node; // 自动推断类型:NodeData['data']['variableName'] const [variableName, setVariableName] = useState(node?.data?.variableName); const onChange = (event: React.ChangeEvent<HTMLInputElement>) => { setVariableName(event.target.value); }; const onSubmit = () => { patchCurrentNode({ data: { variableName: variableName, }, }); // 如果需要在内部使用NodeData的子类型,比如data的类型: // const dataType: NodeData['data'] = node.data; }; 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 || ''} // 避免value为undefined导致的React警告 onChange={onChange} /> <FontAwesomeIcon className={'submit'} icon={['fas', 'paper-plane']} onClick={onSubmit} /> </div> ); }; export default VariableNameInput;
内容的提问来源于stack exchange,提问作者Vadorequest
相关产品推荐
相关产品推荐

