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

使用extendVariants扩展NextUI组件时TypeScript类型提取失败

扩展NextUI Button组件构建npm包时触发TS2742类型错误

我基于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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 15:47:36