# TypeScript (https://www.tailwind-variants.org/docs/typescript)

Extract typed props with VariantProps, work with slotted return types, and keep variant inference reliable.

Tailwind Variants is written in TypeScript. Variant keys and values infer automatically — no code generation required.

## `VariantProps`
Extract the props a recipe accepts:

```tsx
import { tv, type VariantProps } from 'tailwind-variants';

export const button = tv({
  base: 'inline-flex cursor-pointer items-center justify-center rounded-full px-4 py-1.5 font-medium select-none',
  variants: {
    variant: {
      primary: 'bg-zinc-900 text-white',
      secondary: 'bg-zinc-100 text-zinc-900',
      tertiary: 'text-zinc-600'
    },
    flat: {
      true: 'bg-transparent shadow-none'
    }
  },
  defaultVariants: {
    variant: 'primary'
  }
});

type ButtonVariants = VariantProps<typeof button>;
// variant?: "primary" | "secondary" | "tertiary"
// flat?: boolean

interface ButtonProps extends ButtonVariants {
  children: React.ReactNode;
  className?: string;
}

export function Button({ children, className, ...variants }: ButtonProps) {
  return (
    <button className={button({ ...variants, className })}>
      {children}
    </button>
  );
}
```

Keys with `defaultVariants` become optional on the type.

## Required variants
TV does not have a built-in "required variant" flag. Use TypeScript utilities:

```ts
type ButtonVariants = VariantProps<typeof button>;

type RequiredSize = Omit<ButtonVariants, 'size'> &
  Required<Pick<ButtonVariants, 'size'>>;
```

Or model the axis without a default so TypeScript keeps it required.

## Slotted return types
Slotted recipes return an object of slot functions. Destructure once for cleaner types:

```ts
const alert = tv({
  slots: {
    base: 'flex gap-3 rounded-lg p-4',
    title: 'font-semibold',
    description: 'text-sm'
  },
  variants: {
    color: {
      default: {
        base: 'bg-zinc-100',
        title: 'text-zinc-900'
      },
      danger: {
        base: 'bg-red-50',
        title: 'text-red-900'
      }
    }
  }
});

type AlertSlots = ReturnType<typeof alert>;
// AlertSlots.base, .title, .description — each (props?) => string

function Alert({ color }: VariantProps<typeof alert>) {
  const { base, title, description } = alert({ color });

  return (
    <div className={base()}>
      <p className={title()}>Title</p>
      <p className={description()}>Body</p>
    </div>
  );
}
```

## `as const` for external definitions
When variants live outside `tv`, use `as const` so TypeScript preserves literal keys:

```ts
const variants = {
  primary: 'bg-zinc-900 text-white',
  secondary: 'bg-zinc-100 text-zinc-900',
  tertiary: 'text-zinc-600'
} as const;

const button = tv({
  variants: { variant: variants }
});
```
