为@expo/vector-icons的HOC创建关联子组件属性的Props类型
为@expo/vector-icons的HOC实现组件与图标名的类型关联
我正在为@expo/vector-icons编写一个高阶组件(HOC),需要给Props定义通用类型。这个模块里有多个图标源组件(比如MaterialCommunityIcons、MaterialIcons、FontAwesome等),它们结构一致,但每个组件的name属性是各自独有的字面量类型集合。
这个HOC需要接收两个Props:component(图标源组件)和name(图标名称)。我想要实现的是:当指定component后,name能自动推断出该组件对应的可用图标名称列表。
我尝试的代码如下:
const IconLibs = { MaterialCommunityIcons, MaterialIcons, FontAwesome }; type IconLibsNames = keyof typeof IconLibs; type HocProps<Key extends IconLibsNames> = { component: typeof IconLibs[Key]; name: React.ComponentProps<typeof IconLibs[Key]>["name"]; };
但实际效果是所有图标名称被合并成了一个大集合,不管选哪个component,name都能使用所有图标名。请问该如何正确拆分,让name和所选component对应起来?
补充:获取组件对应name的字面量类型有两种方式:
1.
type name = React.ComponentProps<typeof MaterialIcons>["name"]
type name = keyof typeof MaterialIcons.glyphMap
解决方案
问题出在当前泛型定义无法让TypeScript在传入具体组件时自动锁定对应的泛型参数,导致类型推断失效。可以通过联合类型映射的方式,为每个图标源组件生成专属的Props类型,实现component与name的精准关联:
import type { ComponentType } from 'react'; import { MaterialCommunityIcons, MaterialIcons, FontAwesome } from '@expo/vector-icons'; // 定义图标库映射 const IconLibs = { MaterialCommunityIcons, MaterialIcons, FontAwesome }; // 为每个图标组件生成对应的专属Props类型,再合并为联合类型 type IconComponentProps = { [K in keyof typeof IconLibs]: { component: typeof IconLibs[K]; name: React.ComponentProps<typeof IconLibs[K]>['name']; }; }[keyof typeof IconLibs]; // HOC组件使用上述联合类型作为Props约束 const IconHOC = (props: IconComponentProps) => { const { component: Icon, name, ...rest } = props; return <Icon name={name} {...rest} />; }; // 使用示例:指定component后,name会自动提示对应组件的图标名 const Test = () => { return ( <> <IconHOC component={MaterialIcons} name="home" /> {/* 仅MaterialIcons的图标名会被提示 */} <IconHOC component={FontAwesome} name="star" /> {/* 仅FontAwesome的图标名会被提示 */} </> ); };
原理说明
- 通过
[K in keyof typeof IconLibs]遍历所有图标源组件,为每个组件生成一组{ component: 组件类型, name: 对应图标名类型 }的专属Props - 最后通过
[keyof typeof IconLibs]提取所有专属类型的联合类型,TypeScript识别到具体的component时,会自动缩小联合类型范围,从而锁定对应的name类型
可选优化:基于glyphMap的类型定义
如果更倾向于用glyphMap获取图标名类型,可将Props类型修改为:
type IconComponentProps = { [K in keyof typeof IconLibs]: { component: typeof IconLibs[K]; name: keyof typeof IconLibs[K]['glyphMap']; }; }[keyof typeof IconLibs];
内容的提问来源于stack exchange,提问作者Sander
相关产品推荐
相关产品推荐

