Provide A Custom TS-safe Custom Variant
Environment
- Operating System:
Darwin
- Node Version:
v22.12.0
- Nuxt Version:
3.17.1
- CLI Version:
3.25.0
- Nitro Version:
2.11.11
- Package Manager:
pnpm@10.6.2
- Builder:
-
- User Config:
modules
,devtools
,app
,css
,site
,content
,runtimeConfig
,build
,future
,experimental
,compatibilityDate
,typescript
,cookieControl
,eslint
,i18n
,icon
,image
,ogImage
,sitemap
,turnstile
- Runtime Modules:
@vueuse/nuxt@13.1.0
,@nuxt/eslint@1.3.0
,@nuxt/ui-pro@3.1.0
,@nuxtjs/sitemap@7.2.10
,@nuxtjs/robots@5.2.10
,@nuxt/content@3.5.1
,nuxt-og-image@5.1.3
,@nuxt/image@1.10.0
,@nuxtjs/turnstile@1.0.0
,@dargmuesli/nuxt-cookie-control@9.0.2
,@nuxtjs/robots@5.2.10
,nuxt-auth-utils@0.5.20
,@nuxtjs/i18n@9.5.3
- Build Modules:
-
Version
v3.1.0
Reproduction
Not needed for this situation
Description
You're trying to create a custom variant for a button using TypeScript, but you're encountering a TypeScript error when trying to apply it at the component level. You've tried adding the variant using the variants
object and also using compoundVariants
, but you're still getting the error.
Code Snippet
button: {
variants: {
variant: {
gradient: 'shadow-sm shadow-emerald-300/5 border border-emerald-300/10 hover:shadow hover:border-emerald-300 hover:shadow-emerald-300 transition-all duration-200 text-white bg-gradient-to-br from-logo-a via-logo-a-dark to-logo-a focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-{color}-500 dark:focus-visible:outline-{color}-400',
},
},
defaultVariants: {
color: 'neutral',
variant: 'subtle',
},
default: {
color: 'white',
loadingIcon: 'i-mdi-loading',
},
},
Error Message
Type '"gradient"' is not assignable to type '"solid" | "outline" | "soft" | "subtle" | "ghost" | "link" | undefined'.ts-plugin(2322)
Image
Additional Context
No response
Logs
Solution
The issue here is that the variants
object is expecting a specific type, which is a union of string literals. However, you're trying to assign a string value that contains a dynamic value ({color}
).
To fix this, you can create a custom type that extends the existing type, and then use that custom type in your variants
object.
type CustomVariant = 'gradient' | 'solid' | 'outline' | 'soft' | 'subtle' | 'ghost' | 'link';
button: {
variants: {
variant: {
[key in CustomVariant]: string;
},
},
defaultVariants: {
color: 'neutral',
variant: 'subtle',
},
default: {
color: 'white',
loadingIcon: 'i-mdi-loading',
},
},
In this code, we've created a custom type CustomVariant
that extends the existing type. We've then used this custom type in our variants
object.
By doing this, we're telling TypeScript that the gradient
property is of type CustomVariant
, which is a union of string literals. This should fix the TypeScript error you're encountering.
Conclusion
Q: What is the issue with the custom variant I'm trying to create?
A: The issue is that the variants
object is expecting a specific type, which is a union of string literals. However, you're trying to assign a string value that contains a dynamic value ({color}
).
Q: How can I fix this issue?
A: You can create a custom type that extends the existing type, and then use that custom type in your variants
object.
Q: What is the custom type I need to create?
A: You need to create a custom type that extends the existing type. For example, if the existing type is CustomVariant
, you can create a custom type like this:
type CustomVariant = 'gradient' | 'solid' | 'outline' | 'soft' | 'subtle' | 'ghost' | 'link';
Q: How do I use the custom type in my variants
object?
A: You can use the custom type in your variants
object like this:
button: {
variants: {
variant: {
[key in CustomVariant]: string;
},
},
defaultVariants: {
color: 'neutral',
variant: 'subtle',
},
default: {
color: 'white',
loadingIcon: 'i-mdi-loading',
},
},
Q: What is the [key in CustomVariant]
syntax?
A: The [key in CustomVariant]
syntax is called a mapped type. It's a way to create a new type that is based on an existing type. In this case, we're creating a new type that is based on the CustomVariant
type.
Q: Why do I need to use a mapped type?
A: You need to use a mapped type because the variants
object is expecting a specific type, which is a union of string literals. By using a mapped type, we're telling TypeScript that the gradient
property is of type CustomVariant
, which is a union of string literals.
Q: Can I use a mapped type with other types?
A: Yes, you can use a mapped type with other types. For example, if you have a type Color
that is a union of string literals, you can use a mapped type like this:
type Color = 'red' | 'green' | 'blue';
type CustomColor = {
[key in Color]: string;
};
Q: What is the benefit of using a mapped type?
A: The benefit of using a mapped type is that it allows you to create a new type that is based on an existing type. This can be useful when you need to create a type that is similar to an existing type, but with some modifications.
Q: Can I use a mapped type with interfaces?
A: Yes, you can use a mapped type with interfaces. For example, if you have an interface IColor
that has a property color
, you can use a mapped type like this:
interface IColor {
color: string}
type CustomColor = {
[key in keyof IColor]: string;
};
Q: What is the keyof
syntax?
A: The keyof
syntax is called a type query. It's a way to get the type of a property or a method. In this case, we're using keyof
to get the type of the color
property.
Q: Can I use a mapped type with classes?
A: Yes, you can use a mapped type with classes. For example, if you have a class Color
that has a property color
, you can use a mapped type like this:
class Color {
color: string;
}
type CustomColor = {
[key in keyof Color]: string;
};
Q: What is the benefit of using a mapped type with classes?
A: The benefit of using a mapped type with classes is that it allows you to create a new type that is based on an existing class. This can be useful when you need to create a type that is similar to an existing class, but with some modifications.