Skip to content
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

In 0.31.1, when I wanted to export a type in a component, I had to put it in the header #901

Closed
lykl opened this issue Jan 27, 2022 · 10 comments
Labels
question Further information is requested

Comments

@lykl
Copy link

lykl commented Jan 27, 2022

If I write this, I get an error, but if I put it in the header, it says changeLoading is undefined, and if I put changeLoading in the header, I get an error, which makes me very frustrated

export interface ModalExpose {
  changeLoading: typeof changeLoading
}
@johnsoncodehk
Copy link
Member

johnsoncodehk commented Jan 27, 2022

Does changeLoading is a variable in <script setup>? If so you can't export it even writing normal composition api.

<script lang="ts">
import { defineComponent } from 'vue'

export interface ModalExpose {
  changeLoading: typeof changeLoading // Cannot find name 'changeLoading'.ts(2304)
}

export default defineComponent({
  setup(_, { expose }) {
    const changeLoading = 0;
    expose({ changeLoading })
    return { changeLoading }
  },
});
</script>

But I think you don't need to define expose interface, you can use InstanceType to extract exposes properties in script setup already.

A.vue:

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

const count = ref(0)

defineExpose({ count })
</script>

B.vue:

<script lang="ts" setup>
import { ref, onMounted } from 'vue'
import A from './components/A.vue'

const a = ref<InstanceType<typeof A>>()

onMounted(() => {
  a.value?.count // number | undefined
})
</script>

@johnsoncodehk johnsoncodehk added the question Further information is requested label Jan 27, 2022
@lykl
Copy link
Author

lykl commented Jan 27, 2022

No, changeLoading is a function, and if I don't export its type, the editor will warn me

@lykl
Copy link
Author

lykl commented Jan 27, 2022

Now I have to write it this way:

export interface ModalExpose {
  changeLoading: (loading: boolean) => void
}

But how does this relate to function types?
I want to call this function from ref

@johnsoncodehk
Copy link
Member

Please show me your code that when to use export interface ModalExpose, otherwise I can't guess your intentions.

@lykl
Copy link
Author

lykl commented Jan 27, 2022

child.vue

<script lang="ts" setup>
export interface RoleModalExpose {
  open: (Id?: string, ids?: string) => void
}

const dialogFormVisible = ref(false)
const id = ref('')

function open(Id = '', ids = '') {
  id.value = Id
  dialogFormVisible.value = true
}

defineExpose({ open } as RoleModalExpose)
</script>

father.vue

<script lang="ts" setup>
import roleModal, { RoleModalExpose } from '@/components/roleModal.vue'

const roleModalRef = ref<InstanceType<typeof modal> & RoleModalExpose>()

function handleEmpower(index: number, row: Row) {
  roleModalRef.value?.open(row.roleId, row.resourceIds)
}

</script>

If I delete the open type export, the editor will prompt me:
The type '{$: ComponentInternalInstance; $data: {}; $props: Partial&lt; { modelValue: boolean; title: string; asyncClose: boolean; showBtn: boolean; width: string; destroyOnClose: boolean; }&gt; & Omit&lt; . &gt;; . 10 more ... ; $watch(source: string | Function, cb: Function, options? : WatchOptions&lt; . &gt; | undefined): WatchStopHandle; }..." Property 'open' does not exist on

@johnsoncodehk
Copy link
Member

You don't need RoleModalExpose in this case. Try:

child.vue

<script lang="ts" setup>
const dialogFormVisible = ref(false)
const id = ref('')

function open(Id = '', ids = '') {
  id.value = Id
  dialogFormVisible.value = true
}

defineExpose({ open })
</script>

father.vue

<script lang="ts" setup>
import roleModal from '@/components/roleModal.vue'

const roleModalRef = ref<InstanceType<typeof roleModal>>()

function handleEmpower(index: number, row: Row) {
  roleModalRef.value?.open(row.roleId, row.resourceIds)
}

</script>

@lykl
Copy link
Author

lykl commented Jan 27, 2022

Maybe I didn't make it clear, but roleModal components are encapsulated by Modal component.

<template>
  <div>
    <el-dialog
      :title="title"
      v-model="dialogFormVisible"
      @close="cancel"
      :close-on-click-modal="false"
      :width="width"
      :destroy-on-close="destroyOnClose"
    >
      <slot>
        <span></span>
      </slot>
      <template #footer>
        <div class="dialog-footer" v-if="showBtn">
          <el-button @click="close" size="small">cancel</el-button>
          <el-button
            type="primary"
            @click="submit"
            :loading="loading"
            size="small"
            >submit</el-button
          >
        </div>
      </template>
    </el-dialog>
  </div>
</template>

<script lang="ts" setup>
import { debounce } from '@/common/utils'
import { reactive, watch, toRefs } from 'vue'
export interface ModalExpose {
  changeLoading: (loading: boolean) => void
}
const props = defineProps({
  modelValue: {
    type: Boolean,
    default: false,
  },
  title: {
    type: String,
    default: '',
  },
  asyncClose: {
    type: Boolean,
    default: false,
  },
  showBtn: {
    type: Boolean,
    default: true,
  },
  width: {
    type: String || Number,
    default: '50%',
  },
  destroyOnClose: {
    type: Boolean,
    default: false,
  },
})
const { modelValue, title, asyncClose, showBtn, width } = toRefs(props)
const emit = defineEmits(['submit', 'cancel', 'update:modelValue'])
const data = reactive({
  dialogFormVisible: false,
  loading: false,
})
const { dialogFormVisible, loading } = toRefs(data)

function changeLoading(loading: boolean) {
  data.loading = loading
}

const submit = debounce(function () {
  emit('submit')
  if (props.asyncClose) {
    changeLoading(true)
    return
  }
  close()
})

function close() {
  data.dialogFormVisible = false
  emit('update:modelValue', false)
}

const cancel = debounce(function () {
  emit('cancel', false)
  close()
})

watch(
  () => props.modelValue,
  v => {
    data.dialogFormVisible = v
    if (!v) {
      changeLoading(false)
    }
  }
)

defineExpose({
  changeLoading,
} as ModalExpose)
</script>

<style lang="scss" scoped></style>

I found that in the Modal component, changeLoading does remove types.
However, there is no way to remove the definition of open in the roleModal component, and the editor throws errors as described above.
I can't tell the difference between the two that causes the editor to throw an error

@lykl
Copy link
Author

lykl commented Jan 27, 2022

roleModal.vue

<template>
    <modal
      v-model="dialogFormVisible"
      v-bind="modalValue"
      ref="modalRef"
      @submit="submit"
    />
</template>
<script lang="ts" setup>
    const modalRef = ref<InstanceType<typeof modal> & ModalExpose>()
</script>

Here I can delete ModalExpose.

@johnsoncodehk
Copy link
Member

Same with RoleModalExpose, you can just delete ModalExpose.

@lykl
Copy link
Author

lykl commented Jan 27, 2022

Same with RoleModalExpose, you can just delete ModalExpose.

Thank you, I have found the cause of the error, you are right, I might not have found this problem if it was not for volar update.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants