You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为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

关键说明:

  1. 泛型:让组件可以接收任意结构的数组数据,TypeScript会自动根据传入的data推断出T的具体类型。
  2. render类型定义:(item: T, index: number) => React.ReactNode,严格对应数组map方法的回调参数(元素+索引),返回值使用React.ReactNode比JSX.Element更灵活(支持文本、null等更多类型)。
  3. 类型推断优化:使用组件时无需手动声明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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 07:43:22