Skip to content

vue/require-default-prop is not compatible with the Composition API and @typescript-eslint/no-unnecessary-condition #2051

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
2 tasks done
6XGate opened this issue Dec 1, 2022 · 5 comments

Comments

@6XGate
Copy link

6XGate commented Dec 1, 2022

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: 8.28.0
  • eslint-plugin-vue version: 9.8.0
  • Node version: 16.17.0
  • Operating System: Manjaro

Please show your full configuration:

/* eslint-env node */
require('@rushstack/eslint-patch/modern-module-resolution');

/** @type {import('eslint').Linter.Config} */
module.exports = {
  root: true,
  extends: [
    'eslint:recommended'
  ],
  overrides: [
    {
      files: ['*.ts', '*.tsx'],
      extends: [
        'eslint:recommended',
        'plugin:@typescript-eslint/strict'
      ],
      parserOptions: {
        project: './tsconfig.json',
        extraFileExtensions: ['.vue']
      }
    },
    {
      files: ['*.vue'],
      extends: [
        'plugin:vue/recommended',
        'eslint:recommended',
        'plugin:@typescript-eslint/strict',
        '@vue/eslint-config-typescript/recommended'
      ],
      parser: "vue-eslint-parser",
      parserOptions: {
        parser: "@typescript-eslint/parser",
        project: './tsconfig.json',
        extraFileExtensions: ['.vue']
      }
    }
  ]
}

What did you do?

src/components/DisplayMessageCorrect.vue:

<template>
  <div>{{ resultingMessage }}</div>
</template>

<script setup lang="ts">
import { ref, watch } from 'vue';

const props = defineProps({
  // Wants `default` or `required` to ease the `vue/require-default-prop warning`. This is not
  // desirable when the property could be `undefined`. Right now this is typed as
  // `string | undefined`.
  message: String
})

const resultingMessage = ref(props.message ?? 'No message today')

watch(() => props.message, () => { resultingMessage.value = props.message ?? 'No message today'})
</script>

src/components/DisplayMessageWrong.vue:

<template>
  <div>{{ resultingMessage }}</div>
</template>

<script setup lang="ts">
import { ref, watch } from 'vue';

const props = defineProps({
  // Added default, to ease the vue/require-default-prop warning. Now this prop is extracted as
  // `string` and now `string | undefined`
  message: { type: String, default: undefined }
})

// With default defined, ESLint will now trigger `@typescript-eslint/no-unnecessary-condition` warning
const resultingMessage = ref(props.message ?? 'No message today')

// With default defined, ESLint will now trigger `@typescript-eslint/no-unnecessary-condition` warning
watch(() => props.message, () => { resultingMessage.value = props.message ?? 'No message today'})
</script>

What did you expect to happen?
vue/require-default-prop is not included in any of the recommended configurations or treats Composition API built components differently.

Since the Composition API actually understands the following PropOption shapes as also allowing undefined to be the value type seen within the component:

  • String
  • { type: String }
  • { type: String, required: false }
    Adding default or required: true; the only ways to fix the warning from this rule, results in the extracted property type to no longer include undefined, the rule is inappropriate for the Composition API.

What actually happened?
The following code will results in:

src/components/DisplayMessageCorrect.vue
  12:3  warning  Prop 'message' requires default value to be set  vue/require-default-prop

src/components/DisplayMessageWrong.vue
  15:30  warning  Unnecessary conditional, expected left-hand side of `??` operator to be possibly null or undefined  @typescript-eslint/no-unnecessary-condition
  18:61  warning  Unnecessary conditional, expected left-hand side of `??` operator to be possibly null or undefined  @typescript-eslint/no-unnecessary-condition

✖ 3 problems (0 errors, 3 warnings)

Repository to reproduce this issue
https://github.com/6XGate/vue-require-default-prop

@Mister-Hope
Copy link
Contributor

Mister-Hope commented Dec 2, 2022

I personally think at message: { type: String, default: undefined } is not a supported writing in typescript. Your default value should be a string instead of undefined,

If you want the default value when not passing, you shall use xxx: String or xxx: { type: String } and disable the other eslint rule for the line.

@6XGate
Copy link
Author

6XGate commented Jan 20, 2023

This rule should no longer be recommended in Vue v3 rule sets, and probably not v2 with the recent 2.7 (or only off for that version). A default value cannot be specified to maintain the proper property type with JSX/TSX or setup.

@FloEdelmann
Copy link
Member

Shameless plug (and a bit off-topic): This was one of the reasons for me to create the vue-ts-types module. Maybe it's helpful for someone.

@6XGate
Copy link
Author

6XGate commented Jun 26, 2023

Shameless plug (and a bit off-topic): This was one of the reasons for me to create the vue-ts-types module. Maybe it's helpful for someone.

Yeah, I also ended up rigging a translator system for Zod schemas into Vue prop options to expand validation in Vue. As part of that, it worked around this issue as well since the ESLint plug-in wouldn't have any clue what to do with props defined with a propSchema function.

@dmke
Copy link

dmke commented Oct 30, 2023

(condensed version of #2306)

I ought to mention that there's a similar issue with type parameters:

<script setup lang="ts">
const props = withDefaults(defineProps<{
  width: number
  height?: number
}>(), {
  width: 100,
})
</script>

<template>
  {{ props }}
</template>

This also produces a warning ("Prop 'height' requires default value to be set vue/require-default-prop"). I would argue that undefined is a perfectly valid default value, especially since this snippet does not trigger vue/require-default-prop at all:

<script setup lang="ts">
defineProps<{
  width: number
  height?: number
}>()
</script>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants