如何为React函数组件的render属性编写TypeScript类型?
如何为Dropdown组件的render属性编写正确的TypeScript类型?
我是TypeScript新手,编写了一个简单的Dropdown组件,但无法确定render属性的正确类型。
组件代码:
import React from 'react' interface Props{ children: React.ReactNode, data: Object[], render: (a:any)=>JSX.Element } const Dropdown = ({children, data, render}:Props) => { return ( <label className=' relative select-none'> <input type='checkbox' className='hidden peer' /> <div className='flex flex-row gap-2 items-center justify-center cursor-pointer' > {children} </div> <ul className='hidden peer-checked:block list-none py-2 absolute right-0 top-[125%] bg-white dark:bg-gray-700 rounded-md shadow-md px-2 '> { data.map(render) } </ul> </label> ) } const Menu = ({children}: {children: React.ReactNode})=>{ return ( <li className='flex flex-row gap-3 cursor-pointer px-6 hover:bg-gray-300 dark:hover:bg-gray-900 transition duration-300 py-1 rounded-md'> {children} </li> ) } Dropdown.Menu = Menu export default Dropdown
我是这样使用该组件的:
<Dropdown data={ [ {short:'EN', long: 'English'}, {short:'RU', long: 'Русский'}, {short:'TM', long: 'Türkmen'} ] } render={ (row:{short:String, long: String}, index:Number): React.ReactNode => <Dropdown.Menu > <span className='uppercase font-medium'>{row.short}</span> <span>{row.long}</span> </Dropdown.Menu> } > <div className='flex flex-row gap-2 items-center justify-center cursor-pointer' > <span className='uppercase'> En </span> <RxTriangleDown /> </div> </Dropdown>
解决方案:使用泛型实现类型安全
核心思路是通过泛型让组件适配任意数据类型,同时保证render函数的参数类型和data数组的元素类型严格匹配,避免any带来的类型不安全问题。
修改后的完整组件代码:
import React from 'react' // 定义泛型Props接口,T代表data数组的元素类型 interface DropdownProps<T> { children: React.ReactNode; data: T[]; // render函数接收当前元素、索引,返回React节点 render: (item: T, index: number) => React.ReactNode; } // 将Dropdown定义为泛型组件 const Dropdown = <T,>({ children, data, render }: DropdownProps<T>) => { return ( <label className='relative select-none'> <input type='checkbox' className='hidden peer' /> <div className='flex flex-row gap-2 items-center justify-center cursor-pointer'> {children} </div> <ul className='hidden peer-checked:block list-none py-2 absolute right-0 top-[125%] bg-white dark:bg-gray-700 rounded-md shadow-md px-2'> {data.map(render)} </ul> </label> ) } const Menu = ({ children }: { children: React.ReactNode }) => { return ( <li className='flex flex-row gap-3 cursor-pointer px-6 hover:bg-gray-300 dark:hover:bg-gray-900 transition duration-300 py-1 rounded-md'> {children} </li> ) } Dropdown.Menu = Menu export default Dropdown
关键说明:
- 泛型
:让组件可以接收任意结构的数组数据,TypeScript会自动根据传入的 data推断出T的具体类型。 - render类型定义:
(item: T, index: number) => React.ReactNode,严格对应数组map方法的回调参数(元素+索引),返回值使用React.ReactNode比JSX.Element更灵活(支持文本、null等更多类型)。 - 类型推断优化:使用组件时无需手动声明
row的类型,TypeScript会自动从data结构推断出类型。另外注意用string(TypeScript基本类型)替代String(包装对象),这是TS中的最佳实践。
优化后的使用示例:
<Dropdown data={[ { short: 'EN', long: 'English' }, { short: 'RU', long: 'Русский' }, { short: 'TM', long: 'Türkmen' } ]} // row的类型会被自动推断,无需手动声明 render={(row, index) => ( <Dropdown.Menu key={index}> <span className='uppercase font-medium'>{row.short}</span> <span>{row.long}</span> </Dropdown.Menu> )} > <div className='flex flex-row gap-2 items-center justify-center cursor-pointer'> <span className='uppercase'> En </span> <RxTriangleDown /> </div> </Dropdown>
如果需要在复杂场景下显式指定泛型类型,可这样写:
type LanguageOption = { short: string; long: string }; <Dropdown<LanguageOption> data={[...]} render={(row) => ...} > ... </Dropdown>
内容的提问来源于stack exchange,提问作者Bibisoltan Rustamowa
相关产品推荐
相关产品推荐

