如何将图标目录文件名作为TypeScript类型约束Icon组件name属性?
解决方案:为Icon组件的name属性生成文件名联合类型约束
方法一:通过import.meta.glob路径自动推导类型
不需要额外插件导出iconName,直接从import.meta.glob返回的文件路径中提取文件名作为联合类型,步骤如下:
- 导入所有SVG文件
在Icon组件文件中使用import.meta.glob导入目标目录下的所有SVG:
const icons = import.meta.glob('../assets/icons/*.svg', { eager: true });
- 提取文件名联合类型
利用TypeScript的条件类型和infer关键字,从文件路径中解析出不带.svg后缀的文件名:
// 从路径中提取文件名,自动生成联合类型 type IconName = keyof typeof icons extends `../assets/icons/${infer Name}.svg` ? Name : never;
此时IconName会自动生成为目录下所有SVG文件名的联合类型,比如"close" | "collapse" | "delete"。
- 约束组件props类型
将Icon组件的name属性类型设置为IconName:
interface IconProps { name: IconName; size?: string | number; color?: string; // 其他自定义属性 } const Icon = ({ name, ...rest }: IconProps) => { const IconComponent = icons[`../assets/icons/${name}.svg`]?.default; if (!IconComponent) return null; return <IconComponent {...rest} />; }; export default Icon;
方法二:配合Vite插件生成类型声明文件
如果坚持要通过插件导出iconName,可以让插件同时生成类型声明文件,步骤如下:
- 修改Vite插件,添加类型生成逻辑
在Vite插件中,除了给SVG模块添加iconName导出,还收集所有文件名并生成icons.d.ts类型文件:
// vite.config.ts import { defineConfig } from 'vite'; const iconNames: string[] = []; export default defineConfig({ plugins: [ { name: 'svg-icon-name', transform(code, id) { if (id.endsWith('.svg')) { const fileName = id.split('/').pop()?.replace('.svg', '') || ''; iconNames.push(fileName); // 给SVG模块添加iconName导出 return `${code}\nexport const iconName = '${fileName}';`; } return code; }, generateBundle() { // 生成IconName联合类型的声明文件 const dtsContent = `export type IconName = ${iconNames.map(name => `'${name}'`).join(' | ')};`; this.emitFile({ type: 'asset', fileName: 'icons.d.ts', source: dtsContent, }); }, }, ], });
- 在组件中导入类型
直接从生成的声明文件中导入IconName约束props:
import type { IconName } from '../icons.d.ts'; interface IconProps { name: IconName; // 其他属性 } // 组件实现同方法一
注意事项
- 确保
import.meta.glob的路径准确,能匹配到目标目录下所有SVG文件 - 如果文件名包含特殊字符(如kebab-case),TypeScript仍能正确解析为联合类型成员
- 方法一更简洁,无需额外维护插件的类型生成逻辑,推荐优先使用
内容的提问来源于stack exchange,提问作者Aplefull
相关产品推荐
相关产品推荐

