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

TypeScript泛型提取嵌套变体键异常:CVA类构建器类型未展开

解决TypeScript泛型未展开问题,实现CVA风格类构建器

核心问题原因

TypeScript默认会保留泛型的抽象定义,不会自动展开成具体的属性结构,导致VS Code提示里显示的是泛型参数而非{color: string; size: string}这类具体类型。

解决方案步骤

1. 定义类型展开辅助工具

先写一个Prettify类型,强制TypeScript展开泛型为具体对象结构:

type Prettify<T> = {
  [K in keyof T]: T[K]
} & {}

这个类型通过重新映射属性并交叉空对象,能触发TypeScript的类型展开逻辑,让VS Code显示具体的属性键。

2. 正确提取并定义输入类型

假设你的TConfig结构包含variants字段,先定义基础配置类型,再提取输入类型:

// 基础CVA配置类型
type CVAConfig<TVariants extends Record<string, Record<string, string>>> = {
  variants: TVariants
  defaultVariants?: Partial<{ [K in keyof TVariants]: keyof TVariants[K] }>
}

// 从配置中提取的输入类型,用Prettify包裹
type CVAInput<TConfig extends CVAConfig<any>> = Prettify<{
  [K in keyof TConfig['variants']]?: keyof TConfig['variants'][K]
}>

如果你的variant取值不需要严格枚举,只需要string类型,把keyof TConfig['variants'][K]替换成string即可。

3. 完整示例代码

type Prettify<T> = {
  [K in keyof T]: T[K]
} & {}

type CVAConfig<TVariants extends Record<string, Record<string, string>>> = {
  variants: TVariants
  defaultVariants?: Partial<{ [K in keyof TVariants]: keyof TVariants[K] }>
}

function createCVA<TConfig extends CVAConfig<any>>(config: TConfig) {
  return (input?: CVAInput<TConfig>) => {
    // 这里实现类名拼接逻辑
    const classes = []
    if (input) {
      for (const key in input) {
        const variantKey = key as keyof typeof input
        classes.push(config.variants[variantKey][input[variantKey] as keyof typeof config.variants[variantKey]])
      }
    }
    // 合并默认变体
    if (config.defaultVariants) {
      for (const key in config.defaultVariants) {
        classes.push(config.variants[key][config.defaultVariants[key] as keyof typeof config.variants[key]])
      }
    }
    return classes.join(' ')
  }
}

// 使用示例
const buttonBuilder = createCVA({
  variants: {
    color: {
      primary: 'bg-blue-500 text-white',
      secondary: 'bg-gray-500 text-gray-100'
    },
    size: {
      sm: 'text-sm px-3 py-1',
      lg: 'text-lg px-6 py-2'
    }
  },
  defaultVariants: {
    size: 'sm'
  }
})

// 此时VS Code会显示buttonBuilder的输入类型为:{ color?: "primary" | "secondary"; size?: "sm" | "lg" }

内容的提问来源于stack exchange,提问作者Cam Parry

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 23:50:14