Skip to content

Regression with props destructuring and optional props between 10.0.0 and 10.1.0 #2741

Open
@mrleblanc101

Description

@mrleblanc101

Checklist

  • I have tried restarting my IDE and the issue persists.
  • I have read the FAQ and my problem is not listed.

Tell us about your environment

  • ESLint version: 9.26.0
  • eslint-plugin-vue version: 10.1.0
  • Vue version: 3.5.13
  • Node version: v20.18.0
  • Operating System: macOS 15.4.1

Please show your full configuration:

import eslintConfigPrettier from 'eslint-config-prettier';
import prettierPlugin from 'eslint-plugin-prettier';
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';

import withNuxt from './.nuxt/eslint.config.mjs';

export default withNuxt({
    plugins: {
        prettier: prettierPlugin,
    },
    rules: {
        ...prettierPlugin.configs.recommended.rules,
        ...eslintConfigPrettier.rules,
        ...eslintPluginPrettierRecommended.rules,
        'vue/no-bare-strings-in-template': [
            'error',
            {
                attributes: {
                    '/.+/': [
                        'title',
                        'description',
                        'label',
                        'help',
                        'tooltip',
                        'placeholder',
                        'errors',
                        'content',
                        'confirmButtonLabel',
                        'confirmButtonVariant',
                    ],
                    'UTab': ['name'],
                },
                directives: ['v-text'],
            },
        ],
        'vue/v-slot-style': ['error', 'shorthand'],
        'vue/define-macros-order': 'error',
    },
});

What did you do?

<template>
    <Component
        :is="to ? NuxtLink : 'button'"
        :type="!to ? type : null"
        :to="to"
        :class="
            twMerge(
                'relative inline-flex h-14 items-center justify-center gap-2.5 rounded border-2 border-transparent px-8 text-center text-sm font-bold leading-6 text-white transition duration-300 ease-in-out disabled:pointer-events-none disabled:opacity-25 [&[disabled]]:pointer-events-none [&[disabled]]:opacity-25',
                variant === 'info' && 'bg-blue hover:bg-blue-80 focus-visible:bg-blue-80',
                variant === 'primary' && 'bg-teal hover:bg-teal-80 focus-visible:bg-teal-80',
                variant === 'success' && 'bg-green hover:bg-green-80 focus-visible:bg-green-80',
                variant === 'warning' && 'bg-yellow hover:bg-yellow-80 focus-visible:bg-yellow-80',
                variant === 'danger' && 'bg-red hover:bg-red-80 focus-visible:bg-red-80',
                variant === 'secondary' &&
                    'border-gray-30 text-typo hover:bg-black/10 focus-visible:bg-black/10 dark:hover:bg-white/10 dark:focus-visible:bg-white/10',
                (prefixIcon || suffixIcon) && !balanced && size !== 'small' && 'px-6',
                (prefixIcon || suffixIcon) && balanced && 'px-16',
                size === 'large' && 'h-20 gap-5 px-6 text-lg',
                size === 'large' && prefixIcon && 'pr-10',
                size === 'large' && suffixIcon && 'pl-10',
                size === 'small' && 'h-10 gap-2 p-2',
                rounded && 'rounded-full',
                rounded && !$slots.default && 'h-auto rounded-full p-2.5',
                hoverEffect && 'hover:scale-110 focus-visible:scale-110',
                $props.class,
            )
        "
    >
        <Icon
            v-if="prefixIcon"
            :name="prefixIcon"
            :class="
                twMerge(
                    'prefixIcon size-6 shrink-0 text-current',
                    balanced && 'absolute left-4',
                    size === 'large' && 'size-9',
                    iconClasses,
                )
            "
        />
        <slot />
        <Icon
            v-if="suffixIcon"
            :name="suffixIcon"
            :class="
                twMerge(
                    'suffixIcon size-6 shrink-0 text-current',
                    balanced && 'absolute right-4',
                    size === 'large' && 'size-9',
                    iconClasses,
                )
            "
        />
    </Component>
</template>

<script lang="ts" setup>
import { twMerge } from 'tailwind-merge';
import { NuxtLink } from '#components';

type ButtonProps = {
    prefixIcon?: string;
    suffixIcon?: string;
    iconClasses?: string;
    size?: 'default' | 'small' | 'large';
    variant?: 'info' | 'primary' | 'secondary' | 'success' | 'warning' | 'danger';
    rounded?: boolean;
    balanced?: boolean;
    hoverEffect?: boolean;
    class?: string;
};

type TypeButton = {
    type?: 'button' | 'submit' | 'reset';
    to?: never;
};

type TypeLink = {
    type?: never;
    to?: string;
};

const {
    type = 'button',
    variant = 'primary',
    hoverEffect = true,
} = defineProps<ButtonProps & (TypeButton | TypeLink)>();
</script>

What did you expect to happen?
No warning when running npm run lint or inside the VS Code

What actually happened?
Warning when running npm run lint and inside the VS Code.

Just updated from 10.0.0 where I had no eslint error to 10.1.0, and now I have vue/require-default-prop popping everywhere.
If I add the default value, eslint will then complain the the variable are unused since they are only used in the template and not the script part.

Before:
Image

After:
Image

Relates to #2475

Repository to reproduce this issue

https://codesandbox.io/p/devbox/relaxed-dirac-q6tgzp?file=%2Fcomponents%2FUButton.vue
Run npm run lint to see the warnings.
Downgrade to 10.0.0, run again.
There are no warnings.

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs reproNeed a repository that can reproduce the problem, or a link to DEMO.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions