javascriptvue.jseslintvuejs3vue-sfc

Vue 3: `defineProps` are referencing locally declared variables


Why I get warning error message:

defineProps are referencing locally declared variables. eslint(vue/valid-define-props)

when I use custom validator in props with SFC <script setup> mode:

<script setup>
import { validateMarkers } from "./hooks"
import { defineProps } from 'vue'

const props = defineProps({
  markers: {
    type: [Array, Object],
    validator: validateMarkers
  }
})
</script>

My custom validator:

export const validateMarkers = (markers) =>
    isNotEmpty(markers)
        ? isObject(markers)
            ? markerValidator(markers)
            : isNotEmptyArray(markers)
            ? markers.every((marker) => markerValidator(marker))
            : error('Undefined type of prop marker')
        : null

How I can fix this warning?


Solution

  • This warning is intended to prevent this particular usage (from the eslint(vue/valid-define-props) docs):

    <script setup>
      /* ✗ BAD */
      const def = { msg: String }
      defineProps(def)
    </script>
    

    If you actually tried to compile that <script setup> block in a build, the compiler emits this error message that clarifies the reasoning a bit:

    defineProps() in <script setup> cannot reference locally declared variables because it will be hoisted outside of the setup() function. If your component options require initialization in the module scope, use a separate normal <script> to export the options instead.

    However, importing the validator from a module should actually be fine because there's no concern of hoisting local variables here. In fact, it compiles without error (see the JS tab in the SFC playground).

    So I believe this is a false positive that could be ignored with a comment:

    <script setup>
    import { validateMarkers } from "./hooks"
    import { defineProps } from 'vue'
    
    const props = defineProps({
      markers: {
        type: [Array, Object],            👇
        validator: validateMarkers, // eslint-disable-line vue/valid-define-props
      }
    })
    </script>