joe-bell / cva

Class Variance Authority
https://cva.style
Apache License 2.0
5.83k stars 114 forks source link

Unexpected Type Definition for `class` and `className` #254

Open carlosyan1807 opened 10 months ago

carlosyan1807 commented 10 months ago

Describe the bug

I'm encountering an issue while using the cva@1.0.0-beta.1 library in a monorepo setup with shared style variants for both Next.js and Nuxt.js projects. After using Vite to bundle the shared project, the generated TypeScript declaration file contains confusing type definitions for the class and className properties.

It's my learning project, so it includes both next and nuxt.

To Reproduce

// src/variants/ripple.ts
import type { VariantProps } from 'cva'

import { cva } from 'cva'

const rippleVariants = cva({
  base: ['pointer-events-none absolute origin-center animate-ripple rounded-full opacity-0'],
  variants: {
    color: {
      default: 'bg-current',
      neutral: 'bg-neutral',
      primary: 'bg-primary',
      secondary: 'bg-secondary',
      info: 'bg-info',
      success: 'bg-success',
      warning: 'bg-warning',
      danger: 'bg-danger',
    },
  },
})

export type RippleVariantProps = VariantProps<typeof rippleVariants>

export { rippleVariants }
// dist/variants/ripple.d.ts
import type { VariantProps } from '../../node_modules/cva';
declare const rippleVariants: (props?: ({
    color?: "primary" | "secondary" | "neutral" | "info" | "success" | "warning" | "danger" | "default" | undefined;
} & ({
    class?: string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | any | {
        [x: string]: any;
    } | null | undefined)[] | {
        [x: string]: any;
    } | null | undefined)[] | {
        [x: string]: any;
    } | null | undefined)[] | {
        [x: string]: any;
    } | null | undefined)[] | {
        [x: string]: any;
    } | null | undefined)[] | {
        [x: string]: any;
    } | null | undefined)[] | {
        [x: string]: any;
    } | null | undefined)[] | {
        [x: string]: any;
    } | null | undefined)[] | {
        [x: string]: any;
    } | null | undefined)[] | {
        [x: string]: any;
    } | null | undefined)[] | {
        [x: string]: any;
    } | null | undefined)[] | {
        [x: string]: any;
    } | null | undefined;
    className?: undefined;
} | {
    class?: undefined;
    className?: string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | any | {
        [x: string]: any;
    } | null | undefined)[] | {
        [x: string]: any;
    } | null | undefined)[] | {
        [x: string]: any;
    } | null | undefined)[] | {
        [x: string]: any;
    } | null | undefined)[] | {
        [x: string]: any;
    } | null | undefined)[] | {
        [x: string]: any;
    } | null | undefined)[] | {
        [x: string]: any;
    } | null | undefined)[] | {
        [x: string]: any;
    } | null | undefined)[] | {
        [x: string]: any;
    } | null | undefined)[] | {
        [x: string]: any;
    } | null | undefined)[] | {
        [x: string]: any;
    } | null | undefined)[] | {
        [x: string]: any;
    } | null | undefined;
})) | undefined) => string;
export type RippleVariantProps = VariantProps<typeof rippleVariants>;
export { rippleVariants };

my vite.config.ts

import dts from 'vite-plugin-dts'

export default defineConfig({
  build: {
    outDir: 'dist',
    emptyOutDir: true,
    lib: {
      entry: entries,
      formats: ['es'],
    },
    rollupOptions: {
      external: ['cva']
    }
  },
  plugins: [dts()],
})

Expected behavior I'm not sure if this is a problem with cva or if I'm doing something wrong, I want it to generate the right type.

Desktop (please complete the following information):

joe-bell commented 10 months ago

Hmm, please can you private a working reproduction? (not code snippets, but a sandbox of some sorts)

carlosyan1807 commented 10 months ago

https://codesandbox.io/p/sandbox/amazing-galois-2d6j7p

cat dist/variants/ripple.d.ts

danmudd-vox commented 3 months ago

This appears to be caused by ClassValue and ClassArray being recursive types. As ClassValue isn't exported and referenceable, types for components using VariantTypes attempt to reconstruct the ClassValue type definition for their class and className props - but because they don't have a type to refer to, it keeps recursing and generates a crazy nested type definition for 10+ levels of depth to attempt replicate the ClassArray type.

These types look to be very similar to those in clsx, which gets around it by exporting the types. Exporting ClassValue should be enough to fix this (and fixed it in my replication).

MrOxMasTer commented 1 month ago

Hmm, please can you private a working reproduction? (not code snippets, but a sandbox of some sorts)

I don't see why you would want to create your own ClassValue implementation when you're using clsx under the hood anyway. It gives me problems with a type that doesn't exist for cva

image

bigint does not exist in cva

And there's also a problem with ClassValue[] because it doesn't fit even if I use cx: image

and don't export the type from the library

MrOxMasTer commented 1 month ago

added bigint to the clx 2.1.1 version (in April)

https://github.com/lukeed/clsx/releases/tag/v2.1.1