使用extendVariants扩展NextUI组件时TypeScript类型提取失败
我基于NextUI组件构建组件库,从扩展经典Button组件入手。构建npm包时触发一系列TS2742错误,提示MyButton的推断类型依赖@nextui-org/system-rsc、@nextui-org/react-utils、@react-types/shared等内部模块,无法移植,必须添加类型注解,导致组件库使用时失去类型安全性。
最初严格按照NextUI官方文档示例实现,同样触发该错误。
扩展NextUI Button的实现代码
import { extendVariants, Button as NextUiButton } from '@nextui-org/react'; import { ReactNode, Ref } from 'react'; import { type VariantProps } from 'tailwind-variants'; const MyButton = extendVariants(NextUiButton, { variants: { // <- modify/add variants color: { primary: 'bg-blue-500 text-black', secondary: 'bg-purple-500 text-black', }, isDisabled: { true: 'bg-[#eaeaea] text-[#000] opacity-50 cursor-not-allowed', }, size: { xs: 'px-unit-2 min-w-unit-12 h-unit-6 text-tiny gap-unit-1 rounded-small', md: 'px-unit-4 min-w-unit-20 h-unit-10 text-small gap-unit-2 rounded-small', xl: 'px-unit-8 min-w-unit-28 h-unit-14 text-large gap-unit-4 rounded-medium', }, }, defaultVariants: { // <- modify/add default variants color: 'primary', size: 'xl', }, compoundVariants: [ // <- modify/add compound variants { isDisabled: true, color: 'secondary', class: 'bg-[#84cc16]/80 opacity-100', }, ], }); interface MyButtonProps extends VariantProps<typeof MyButton> { ref?: Ref<HTMLButtonElement>; className?: string; children?: ReactNode; label: string; } export const Button = ({ size, className, color, label, children, ref }: MyButtonProps) => { return ( <MyButton {...{ ref, className, size, color, type: 'button', }}> {children} {label} </MyButton> ); };
Vite构建配置
import {defineConfig} from 'vite' import react from '@vitejs/plugin-react' import dts from 'vite-plugin-dts' import {libInjectCss} from 'vite-plugin-lib-inject-css' import {extname,relative,resolve} from 'path' import {fileURLToPath} from 'node:url' import {glob} from 'glob' export default defineConfig({ plugins: [react(),libInjectCss(),dts({include: ['stories/components']})], build: { copyPublicDir: false, lib: { entry: resolve(__dirname,'stories/main.ts'), formats: ['es'], name: "my-components", }, rollupOptions: { external: ['react','react/jsx-runtime',"tailwind-variants","framer-motion",RegExp("^(@nextui-org/).+")], input: Object.fromEntries( glob.sync('stories/**/*.{ts,tsx,js,jsx}',{ignore: 'stories/**/*.stories.{ts,tsx,js,jsx}'}).map(file => [ relative( 'stories', file.slice(0,file.length-extname(file).length) ), fileURLToPath(new URL(file,import.meta.url)) ]) ), output: { assetFileNames: 'assets/[name][extname]', entryFileNames: '[name].js', } }, }, })
原生tailwind-variants实现的正常Button代码
用tailwind-variants从零实现Button组件时一切正常,代码如下:
import { ReactNode, Ref } from 'react'; import { tv, type VariantProps } from 'tailwind-variants'; const button = tv({ base: 'font-medium bg-blue-500 text-white rounded-full active:opacity-80', variants: { color: { primary: 'bg-blue-500 text-white', secondary: 'bg-purple-500 text-white', }, size: { sm: 'text-sm', md: 'text-base', lg: 'px-4 py-3 text-lg', }, }, compoundVariants: [ { size: ['sm', 'md'], class: 'px-3 py-1', }, ], defaultVariants: { size: 'md', color: 'primary', }, }); interface ButtonProps extends VariantProps<typeof button> { ref?: Ref<HTMLButtonElement>; className?: string; children?: ReactNode; label: string; } export const Button = ({ size, className, color, label, children, ref }: ButtonProps) => { return ( <button {...{ ref, type: 'button', title: label, className: button({ className, color, size }), }}> {children} {label} </button> ); };
内容的提问来源于stack exchange,提问作者J4v4Scr1pt
相关产品推荐
相关产品推荐

