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

如何根据函数参数配置映射组件Props的正确函数返回类型

类型问题:如何让组件映射函数返回正确的Props类型

我有一个接收固定配置对象的函数,配置对象示例如下:

const config = {
  foo: {
    component: MyComponent1,
  },
  bar: {
    component: MyComponent2,
  },
};

我希望该函数返回如下结构的对象:

const returned = {
  foo: WrappedMyComponent1,
  bar: WrappedMyComponent2,
};

其中ComponentN与WrappedComponentN拥有相同的Props(组件类型基本一致)。

以下是我的尝试代码:

import { Box, TextField } from '@mui/material';

const remapComponents = <P, C extends Record<string, { component: ComponentType<P> }>, R extends Record<keyof C, ComponentType<P>>>(config: C): R =>
  Object.entries(config).reduce((prevValue, [key, conf]) => ({
    ...prevValue,
    [key]: (conf as { component: ComponentType<P> }).component,
  }), EMPTY_DATA_OBJ as R);

const result = remapComponents({ foo: { component: TextField }, bar: { component: Box } });

目前我能获取到result中正确的键(如result.foo、result.bar),但所有组件的类型均为ComponentType<unknown>,请问如何让返回的组件拥有正确的Props类型?


解决方案

问题出在泛型定义过于单一,P被强制作为所有组件共用的Props类型,无法区分每个组件的独立Props。需要让泛型针对配置对象的每个键,单独推断对应组件的Props类型,而非用统一的P约束。

优化后的代码

import { ComponentType } from 'react';
import { Box, TextField } from '@mui/material';

// 定义单个配置项的类型
type ComponentConfig<T extends ComponentType<any>> = {
  component: T;
};

// 核心映射函数
const remapComponents = <Config extends Record<string, ComponentConfig<any>>>(
  config: Config
): { [K in keyof Config]: Config[K]['component'] } => {
  return Object.entries(config).reduce((acc, [key, conf]) => {
    acc[key as keyof Config] = conf.component;
    return acc;
  }, {} as { [K in keyof Config]: Config[K]['component'] });
};

// 使用示例
const result = remapComponents({ 
  foo: { component: TextField }, 
  bar: { component: Box } 
});

// 此时类型推断完全正常:
// result.foo 对应 ComponentType<TextFieldProps>
// result.bar 对应 ComponentType<BoxProps>

关键改进点

  1. 移除统一泛型P:让TypeScript自动推断每个配置项中组件的原生Props类型,避免强制统一导致的unknown丢失。
  2. 精准映射返回类型:用{ [K in keyof Config]: Config[K]['component'] }明确告知TS,返回对象的每个键类型与原配置中对应键的component类型完全一致,完整保留原组件的Props信息。
  3. 安全类型断言:在reduce中使用key as keyof Config确保键的类型合法性,避免不必要的类型损耗。

若需添加组件包装逻辑

如果实际需求是给每个组件添加统一包装(比如示例中的WrappedMyComponent),只需在映射时替换为包装后的组件,类型会自动继承原组件Props:

const wrapComponent = <T extends ComponentType<any>>(Component: T) => {
  return (props: React.ComponentProps<T>) => {
    // 这里可添加统一包装逻辑,比如外层套容器、添加通用事件等
    return <Box padding={1}><Component {...props} /></Box>;
  } as T; // 断言为原组件类型,保留Props推断能力
};

const remapComponents = <Config extends Record<string, ComponentConfig<any>>>(
  config: Config
): { [K in keyof Config]: Config[K]['component'] } => {
  return Object.entries(config).reduce((acc, [key, conf]) => {
    acc[key as keyof Config] = wrapComponent(conf.component); // 替换为包装后的组件
    return acc;
  }, {} as { [K in keyof Config]: Config[K]['component'] });
};

内容的提问来源于stack exchange,提问作者Николай Жигалин

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 05:20:27