Skip to content

Conversation

@NoelDeMartin
Copy link
Contributor

@NoelDeMartin NoelDeMartin commented Dec 4, 2021

Currently, the public API for a component defined using defineExpose is not typed when this component is used elsewhere (as discussed in #4397).

My current workaround is to define the API as an interface and casting the object in the component:

MyComponent.d.ts

export interface IMyComponent { doSomething(): void; }

MyComponent.vue

<script setup lang="ts">  import type { IMyComponent } from './MyComponent';   const publicAPI: IMyComponent = {  doSomething() {  // ...  },  };   defineExpose(publicAPI); </script>

But writing that is cumbersome, I'd prefer doing the following:

defineExpose<IMyComponent>({ doSomething() { // ... }, });

So that's what this PR allows :). Given that it's optional, it shouldn't affect people who isn't using this approach.

I tried extracting this pattern into my own method which I called definePublicAPI, but I was getting a warning so I guess defineExpose cannot be used for composition.

@PurpleTape
Copy link

Hello!

This does not cover the case when ref values passed to defineExpose(). And as result u can't use same interface for expose and access.

MyComponent.d.ts

import type { Ref } from 'vue'; export interface IMyComponent { something: Ref<string>; }

MyComponent.vue

<script setup lang="ts">  import { ref } from 'vue';  import type { IMyComponent } from './MyComponent';   const something = ref('some string');   defineExpose<IMyComponent>({  something,  }); </script>

As docs says: "refs are automatically unwrapped just like on normal instances"
(https://vuejs.org/api/sfc-script-setup.html#defineexpose)

Elsewhere

<tenplate> <MyComponent ref="component"/> </template> <script setup lang="ts"> import { onMounted } from 'vue'; import { MyComponent, IMyComponent } from './MyComponent'; const component = ref<IMyComponent>(); onMounted(() => { console.log(component.value.something) // now it's string but IMyComponent says this is Ref<string> }) </script>
@albertjin
Copy link

albertjin commented Mar 22, 2022

@PurpleTape

A workaround is to add the string type as follows. I hope this weakness of the <script setup> syntax should be revolved in a clean way but not this or that way of workaround.

import type { Ref } from 'vue'; export interface IMyComponent { something: string | Ref<string>; }
@PurpleTape
Copy link

@albertjin, thank you!

I came to a similar decision. If anyone is interested:

// Keep types somewhere import { Ref, ComputedRef, WritableComputedRef, UnwrapNestedRefs, } from 'vue'; export type VueRef<T> = Ref<T> | ComputedRef<T> | WritableComputedRef<T>; export type VueUnwrapRef<T> = UnwrapNestedRefs<T>; // Component types import { VueRef, VueUnwrapRef } from '@/types'; export interface IComponentWrapped { value: VueRef<string>; } export type IComponent = VueUnwrapRef<IComponentWrapped>; // Component import { ref } from 'vue'; import { IComponentWrapped } from './component-types'; const value = ref('something'); defineExpose<IComponentWrapped>({ value, }) // Usage import { ref } from 'vue'; import { IComponent } from '@/component/component-types'; const myComponent = ref<IComponent>(); console.log(myComponent.value) // 'something'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

4 participants