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

React+TS:如何同时使用点标记组件与泛型?

问题描述

我有一个名为List的组件,它通过点标记声明了子组件Title,初始实现代码如下:

type Item = {
  name: string;
  value: number;
};

type ListProps = {
  title: string;
  data: Item[];
};

export const List: React.FC<ListProps> & {
  Title: React.FC<{ title: string }>
} = ({ title, data }) => {
  return (
    <div>
      <List.Title title={title} />
      <ul>
        {data.map((item) => (
          <li>
            {item.name}: {item.value}
          </li>
        ))}
      </ul>
    </div>
  );
};

List.Title = ({title}) => <div>{title}</div>

使用时一切正常:

<List
    title="Stats"
    data={[
        { name: "strength", value: 30 },
        { name: "dexterity", value: 50 },
        { name: "intellect", value: 100 },
        { name: "vitality", value: 40 }
    ]}
/>

但当我想给组件添加泛型参数,让item.name可以是特定类型并从父组件传入时,遇到了类型问题。比如期望这样使用:

<List<"strength" | "dexterity" | "intellect" | "vitality">
    title="Stats"
    data={[
        { name: "strength", value: 30 },
        { name: "dexterity", value: 50 },
        { name: "intellect", value: 100 },
        { name: "vitality", value: 40 }
    ]}
/>

我尝试了泛型写法,但无法给List.Title正确指定类型:

type Item<ItemName extends string = string> = {
  name: ItemName;
  value: number;
};

type ListProps<ItemName extends string = string> = {
  title: string;
  data: Item<ItemName>[];
};

export const List = <ItemName extends string = string,>({ title, data }: ListProps<ItemName>): JSX.Element => {
  return (
    <div>
      <ul>
        {data.map((item) => (
          <li>
            {item.name}: {item.value}
          </li>
        ))}
      </ul>
    </div>
  );
};

// 此处无法正常工作,因为List没有Title属性
// List.Title = ({ title }) => <div>{title}</div>;

请问如何将泛型与点标记子组件这两种技术结合使用?


解决方案

可以通过显式定义泛型组件的类型结构,把Title子组件的类型包含进去,同时保留主组件的泛型能力。具体实现如下:

import React from 'react';

// 定义泛型Item类型
type Item<ItemName extends string = string> = {
  name: ItemName;
  value: number;
};

// 定义泛型ListProps类型
type ListProps<ItemName extends string = string> = {
  title: string;
  data: Item<ItemName>[];
};

// 定义完整的List组件类型:泛型FC + Title子组件类型
type ListComponent = {
  <ItemName extends string = string>(props: ListProps<ItemName>): JSX.Element;
  Title: React.FC<{ title: string }>;
};

// 实现主组件
const ListBase = <ItemName extends string = string>({ title, data }: ListProps<ItemName>) => {
  return (
    <div>
      <List.Title title={title} />
      <ul>
        {data.map((item, index) => (
          <li key={index}>
            {item.name}: {item.value}
          </li>
        ))}
      </ul>
    </div>
  );
};

// 组装完整的List组件,挂载Title子组件
export const List: ListComponent = ListBase as ListComponent;
List.Title = ({ title }) => <div>{title}</div>;

这样就可以正常使用泛型版本的List组件,同时保留List.Title的点标记调用:

<List<"strength" | "dexterity" | "intellect" | "vitality">
    title="Stats"
    data={[
        { name: "strength", value: 30 },
        { name: "dexterity", value: 50 },
        { name: "intellect", value: 100 },
        { name: "vitality", value: 40 }
    ]}
/>

关键说明

  • 单独定义ListComponent类型,它包含一个泛型函数(对应主组件)和Title子组件的类型声明。
  • 先实现主组件ListBase,再将其断言为ListComponent类型,最后挂载Title子组件。
  • 这种方式既保留了泛型的类型约束能力,又维持了点标记子组件的调用方式。

内容的提问来源于stack exchange,提问作者Max Green

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 01:13:59