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

ref returned by toRefs(props) have type Ref<X | undefined> | undefined #6420

Closed
Waujito opened this issue Aug 7, 2022 · 23 comments · Fixed by #6421 · May be fixed by #8486
Closed

ref returned by toRefs(props) have type Ref<X | undefined> | undefined #6420

Waujito opened this issue Aug 7, 2022 · 23 comments · Fixed by #6421 · May be fixed by #8486

Comments

@Waujito
Copy link

Waujito commented Aug 7, 2022

Vue version

latest, 3.2.37

Link to minimal reproduction

https://codesandbox.io/s/vigorous-sound-6m9x2d

Steps to reproduce

Check sandbox link provided in minimal reproduction field and run npm run type-check in terminal:
изображение

What is expected?

Expected that the type-check action returns no error in buggedComponent.vue that is
the type of optionalProperty in anyProperty assignation detects as string because all conditions that may detect and prevent undefined type are passed:
изображение

What is actually happening?

typescript compiler detects type of the optionalProperty in anyProperty assign condition as string|undefined

System Info

No response

Any additional comments?

If you dont want to check the reproduction link, there are some photos that describes the problem:изображение
изображение
изображение

@Waujito Waujito changed the title toRef() and toRefs() types discrepancy on optional properties type-check cannot detect undefined type on optional properties that are assigned by toRef() and toRefs() Aug 7, 2022
@LinusBorg
Copy link
Member

toRef() works as expected. The prop is possibly undefined, so the generated ref possibly contains and undefined value: Ref<string | undefined>. The propblem is only with toRefs()

For toRefs(), this is a bit trickier and I'd need our TS experts like @pikax to chime in. it seems that the types of toRefs() return Ref<Type | undefined> | undefined for a possibly undefined props, which is not accurate in this scenario - the prop object will always have this key (its value just may be undefined), so we know we will always have get a Ref<string | udnefined>.

However, I think the core issue is TS rather recent differentiation between an optional property and a property whose value is undefined. The following change to the reproduction's code generates the expected type for the ref:

const props = defineProps<{
  requiredProperty: string;
  optionalProperty: string | undefined;
}>();

...and it's better reflecting reality as well: the prop can't be missing on that props object, it can only be undefined. However, this is not a proper workaround for now either, as that so-defined prop can no longer be left out when being used in a parent:

Untitled

And from this perspective, the prop should really be optional, not just its valzue possibly undefined - we want to be able to completely omit it in the parent - but the property key should exist internally on the propsobject for consistency.

Tricky 🤔

@pikax
Copy link
Member

pikax commented Aug 7, 2022

For toRefs(), this is a bit trickier and I'd need our TS experts like @pikax to chime in. it seems that the types of toRefs() return Ref<Type | undefined> | undefined for a possibly undefined props, which is not accurate in this scenario - the prop object will always have this key (its value just may be undefined), so we know we will always have get a Ref<string | udnefined>.

This is only valid for props, but toRefs is used in on the user land, I believe the current toRefs definition is accurate for the type coming from defineProps.

I think we can update the return type from props to remove the optional property, props can be converted to options when we create the PublicComponent type (aka $props).

@Waujito
Copy link
Author

Waujito commented Aug 7, 2022

toRef() works as expected. The prop is possibly undefined, so the generated ref possibly contains and undefined value: Ref<string | undefined>. The propblem is only with toRefs()

I agree. But, I think, the main topic of this issue is that it is impossible to separate Ref<string> from Ref<undefined>

@LinusBorg
Copy link
Member

LinusBorg commented Aug 7, 2022

But, I think, the main topic of this issue is that it is impossible to separate Ref from Ref

If you have an optional prop (= it can be undefined), then a ref created from that optional prop will possibly contain undefined. That's not a bug, and Vue can't solve that for you - you need to do that.

if you have a piece of code that expects Ref<string>, then check the ref's value before calling that code. How do do that exactly is depending on your code (for details please don't use this issue, ask the community on discord or in this repo's discussions tab). Pseudocode:

const myProp: Ref<string | undefined> = toRef(props, 'myProp')

function myFn (myProp: Ref<string>) { ... }

if (myProp.value) {
  myFn(myProp)
}

you would need to do the exact same thing for a possibly undefined plain variable (const myVar: string | undefined)

@LinusBorg
Copy link
Member

This is only valid for props, but toRefs is used in on the user land, I believe the current toRefs definition is accurate for the type coming from defineProps.

True, forgot to mention it, but was aware

I think we can update the return type from props to remove the optional property, props can be converted to options when we create the PublicComponent type (aka $props).

Great, agreed.

@Waujito

This comment was marked as resolved.

@pikax

This comment was marked as resolved.

@Waujito

This comment was marked as resolved.

@pikax

This comment was marked as resolved.

@Waujito

This comment was marked as resolved.

@LinusBorg

This comment was marked as resolved.

@pikax

This comment was marked as resolved.

@LinusBorg

This comment was marked as resolved.

@Waujito

This comment was marked as resolved.

@LinusBorg

This comment was marked as resolved.

@Waujito

This comment was marked as resolved.

@LinusBorg LinusBorg changed the title type-check cannot detect undefined type on optional properties that are assigned by toRef() and toRefs() ref returned by toRefs(props) have type Ref<X | undefined> | undefined Aug 7, 2022
@yangliguo7
Copy link

same problem。
But I found something even weirder
If you dont pass in a variable to defineProps, This property will not be undefined after torefs
The code like

const props = defineProps({
form: {
    type: Object,
    required: true,
  },
  formOptions: {
    type: Array as PropType<string[]>,
    required: true,
  },
});
const { form, formOptions } = toRefs(props);

image

This type will not be infer to undefined

@LinusBorg
Copy link
Member

This type will not be infer to undefined

because you set required: true

@yangliguo7
Copy link

This type will not be infer to undefined

because you set required: true

but . if I code like this。the type will be undefined

const aaa = {
  form: {
    type: Object,
    required: true,
  },
  formOptions: {
    type: Array as PropType<FormOptionItem[]>,
    required: true,
  },
}

const props = defineProps(aaa);

const { form, formOptions } = toRefs(props);

image

@LinusBorg
Copy link
Member

const aaa = {
  form: {
    type: Object,
    required: true,
  },
  formOptions: {
    type: Array as PropType<FormOptionItem[]>,
    required: true,
  },
} as const

without as const, TS will infer the required prop as boolean, meaning it could be true or false.

@ivanmem
Copy link

ivanmem commented Jun 1, 2023

Actual in vue 3.3.4

@wlee221
Copy link

wlee221 commented Jul 10, 2023

As a workaround for now, I'm casting the return type of toRefs manually:

const { prop1, prop2 } = toRefs(props) as Required<ReturnType<typeof toRefs<PropInterface>>>;

@ivanmem
Copy link

ivanmem commented Jul 14, 2023

As a workaround for now, I'm casting the return type of toRefs manually:

const { prop1, prop2 } = toRefs(props) as Required<ReturnType<typeof toRefs<PropInterface>>>;

image
In this case, type is lost.

@github-actions github-actions bot locked and limited conversation to collaborators Nov 24, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
6 participants