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

如何定义类型安全的TypeScript React组件包装器集合WrapperSet

解决方案

核心思路

你之前尝试的思路无法生效的核心原因是:TypeScript 无法对普通数组类型的每个独立元素做不同的泛型参数推断,只能统一将泛型参数收敛为 any 或 unknown。我们可以通过泛型辅助构造函数+可变元组泛型的方案,让 TypeScript 逐个推断数组中每个包装器项的类型,分别校验组件和对应 props 的匹配关系,全程不需要手动声明类型。

完整实现代码

import * as React from "react";

// 定义单个包装器项的类型:对应任意组件和它的props(自动排除wrap函数内部传入的children,用户无需手动传)
type WrapperEntry<P = any> = [
  React.ComponentType<P>,
  Omit<P, "children">
];

// 泛型辅助构造函数:自动推导每个包装器项的类型并做校验
const defineWrappers = <T extends WrapperEntry[]>(wrappers: [...T]) => wrappers;

// wrap函数实现,不需要额外改类型
function wrap(children: JSX.Element, wrappers: WrapperEntry[]): JSX.Element {
  let content = children;
  wrappers.forEach(([ComponentType, props]) => {
    content = <ComponentType {...props}>{content}</ComponentType>;
  });
  return content;
}

// 组件定义和之前一致
const WrapperA: React.FC<{ label: string }> = ({ label, children }) => (
  <>
    <div>WrapperA: {label}</div>
    {children}
  </>
);

const WrapperB: React.FC<{ title: string }> = ({ title, children }) => (
  <>
    <div>WrapperB: {title}</div>
    {children}
  </>
);

// 使用示例
export const Container: React.FC = () => {
  // 用defineWrappers包裹数组,无需手动加类型标注,自动完成类型校验
  const wrappers = defineWrappers([
    [WrapperA, { label: "WrapperA" }], // 类型正确,无报错
    [WrapperB, { title: "foo" }], // 类型正确,无报错
    // 示例:写错props会直接触发TS报错
    // [WrapperA, { title: "错误属性" }] // 报错:对象字面量只能指定已知属性,"title"不在类型"Omit<{ label: string; children?: React.ReactNode; }, 'children'>"中
  ]);

  const content = <span>Original content</span>;
  return wrap(content, wrappers);
};

兼容性说明

该方案完全兼容你使用的 TypeScript 4.3 版本,依赖的可变元组泛型、Omit 工具类型均在 TS4.3 之前已稳定支持。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 22:39:03