Subscribe on changes!

`withDefaults` shows error when using `generic`

avatar
May 14th 2023

Vue version

3.3.2

Link to minimal reproduction

https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgEwKYDNgDtUAUoRgDOANHAO7AwAWAIhgIYCuANjEXAL5zoEhwByAG5NUAgFDj0TLAGMYwCFh4MA1qgDCEcEtRYYAHgAqcVAA8Ye5ByIwo2AOZwAPnCysWAPgAUASkTicEFwUKgwTFDKlDT06MxsRN5omDj4hEQGCIHBOZa2AFxwRtlBnD6+ZFk5uagFcH5wALyegnkwEjmcvuKc4kA

Steps to reproduce

import { defineProps, withDefaults } from 'vue'

function fakeComponent<T extends string | null>() {
    return withDefaults(defineProps<{
        test: T
    }>(), {
        test: () => 'test'  as T // this is error 
    })
}

Or

<script lang="ts" generic="T extends string | null">
withDefaults(defineProps<{
    test: T
  }>(), {
    test: () => 'test' as T // this is error 
})
</script>

What is expected?

The error not to be showing, like the non generic version:

import { defineProps, withDefaults } from 'vue'

function fakeComponent() {
    return withDefaults(defineProps<{
        test: string | null
    }>(), {
        test: () => 'test' as T
    })
}

Or

<script lang="ts" generic="T extends string | null">
withDefaults(defineProps<{
    test: T
  }>(), {
    test: () => 'test'  as T
})
</script>

What is actually happening?

withDefaults is not accepting the generic type

System Info

No response

Any additional comments?

No response

avatar
May 14th 2023

I'm having the same issue using Vue@3.3.0

type Props = {
  options: T[];
  valueKey: keyof T;
  displayKey: keyof T;
  multiple?: boolean;
  closeMenu?: boolean;
};

const props = withDefaults(defineProps<Props>(), {
  multiple: false,
  closeMenu: false,
});

Screenshot 2023-05-15 at 8 46 05 am

avatar
May 16th 2023

@pikax This is work as intended

For example:

function someFunction<T extends string | null>(
    test: T = 'test'
) {
}
Type '"test"' is not assignable to type 'T'.
  '"test"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'string | null'.ts(2322)
(parameter) test: T extends string | null

So withDefaults should throw a error for this, also.

avatar
May 16th 2023

Can't generic and withDefaults be used together?

avatar
May 16th 2023

There is also a BUG, InstanceType<typeof genericComponent>, which is also problematic Dingtalk_20230516104144

avatar
May 16th 2023

There is also a BUG, InstanceType<typeof genericComponent>, which is also problematic Dingtalk_20230516104144

+1

avatar
May 16th 2023

Same error, this should be documented if it is intended. At least they don't show an example in the official documentation.

avatar
May 16th 2023

Same error, this should be documented if it is intended.

@pikax 's demo is TypeScript's feature. Not Vue's feature.

avatar
May 16th 2023

@an501920078 The problem you are having is not the same as the problem with pikax. It is recommended to file an issue separately.

avatar
May 16th 2023

Same error, this should be documented if it is intended.

@pikax 's demo is TypeScript's feature. Not Vue's feature.

But withDefaults is a Vue function/feature isn't it? Why should TS fix this? I mean: it works when I don't use Vue and it's withDefaults function (when I create my own function that defines a default object for example.. TS is able to do that)

If this fails on purpose because TypeScript is unable to do it, it should rather be documented in Vue because the mayority is Vue code here:

<script setup lang="ts" generic="T">
const props = withDefaults(defineProps<{
    modelValue: T;
    title?: string;
}>(), {
    title: 'Hello',
});
</script>
avatar
May 16th 2023

@pikax 's demo is work as intended. @Anubarak Your demo is a Vue error, not work as intended and different with @pikax 's demo. Maybe we should create a separately issue.

avatar
May 16th 2023

@an501920078 The problem you are having is not the same as the problem with pikax. It is recommended to file an issue separately. Sorry, because github where I am is unstable, so it is troublesome to submit it alone. Please check whether others have submitted this problem later

avatar
May 16th 2023

@pikax 's demo is work as intended. @Anubarak Your demo is a Vue error, not work as intended and different with @pikax 's demo. Maybe we should create a separately issue.

Okay, there is already an issue but you closed it and the other issue referenced this one. That's why I thought we should continue here. Sorry my mistake

avatar
May 16th 2023
<script setup lang="ts" generic="T extends { a: string }">
const props = withDefaults(
    defineProps<{
        foo?: T;
    }>(),
    {
        foo: () => ({ a: '' })
    },
);

const emit = defineEmits<{
    (event: 'update:foo', foo: T): void;
}>();

function onClick(){
    emit('update:foo',props.foo)
}
</script>
<template>
    <div @clicko="onClick" >hello, the comp</div>
</template>
<script setup lang="ts" >
import TheComp from './TheComp.vue'
const foo:{ a: string, b: number }|undefined = undefined
const console = globalThis.console
</script>
<template>
    <TheComp :foo="foo" @update:foo="console.log($event.b.toString())"/>
</template>

This will throw an exception. So we can't use code as @pikax 's initial demo

<script lang="ts" generic="T extends string | null">
withDefaults(defineProps<{
    test: T
  }>(), {
    test: () => 'test'
})
</script>

If you don't care about type safety very much, and allow a small part of type unsafety to appear, you can consider

<script lang="ts" generic="T extends string | null">
withDefaults(defineProps<{
    test: T
  }>(), {
    test: () => 'test' as T // UNSAFE code, if don't care about type safety here, use at your own risk.
})
</script>

But currently, the later code also throw a error. Maybe Vue should fix it.

avatar
May 16th 2023

@xiaoxiangmoe So in the end, all comes down to withDefaults is not accepting the generic type. I'm not entirely sure what your issue is or why you don't want to accept the problem we have. We are just talking about the fact, that you can't use generics along with withDefaults. In the end all your examples don't work - that's our issue and what we are trying to explain.

avatar
May 16th 2023

I'm not entirely sure what your issue is or why you don't want to accept the problem we have.

<!--  the original demo -->
<script lang="ts" generic="T extends string | null">
withDefaults(defineProps<{
    test: T
  }>(), {
    test: () => 'test'
})
</script>

@Anubarak If the original demo is supported, it will introduce serious type safety issues, so I express my strong disapproval. And then I discuss with @pikax to change his demo from 'test' to 'test' as T.

I'm very sorry, before communicating with pikax, I didn't notice that there is such an unsafe use requirement of as T and some user don't care about type safety in some default props.This is my mistake, here I apologize to you.

that you can't use generics along with withDefaults.

that's our issue and what we are trying to explain.

I think the usage in https://github.com/vuejs/core/issues/8331 is the reasonable and legal usage. And it have a better demostration about can't use generics along with withDefaults, maybe more people need this usage.

avatar
May 16th 2023

@xiaoxiangmoe I guess we talk about two different things or maybe I don't understand what you mean at all. It's not about the correct usage of TS and it's not about how TS should be used. That's not the point here. It's simply about the fact that you cannot use withDefaults with generics no matter how many times you cast your variables or what you do.

Because no demo here works... I always get the very same error image

Even with casting image

I would be happy if you could provide a working example - even if it's not type save or ugly or whatever but at least an example that does not throw the Argument of type X is not assignable to parameter of type 'InferDefaults

avatar
May 17th 2023

After I update to 3.3.2 same error occurred:

Screenshot 2023-05-17 110650

When I use interface instead of type there are error too:

image

avatar
May 17th 2023

@Dobril 3.3.2 was 5 days ago, the pull request here is not yet in a release.

@pikax thank you very much for your efforts to fix it

avatar
May 18th 2023

@Dobril 3.3.2 was 5 days ago, the pull request here is not yet in a release.

@Anubarak, what is the planned release for this fix?

avatar
May 18th 2023

@rodrigocfd it was realeased on 3.3.3

avatar
May 18th 2023

@pikax what am I doing wrong here?

gen-err

Reproducible:

<script setup lang="ts" generic="T">
const props = withDefaults(defineProps<{
    value?: T | null;
    list: T[];
}>(), {
    value: null,
});
</script>

<template>
    <select>
        <option v-for="item of props.list">

        </option>
    </select>
</template>

Using:

  • Vue: 3.3.4
  • Volar: 1.6.4
avatar
May 19th 2023

@rodrigocfd Drop the props. from props.list, since the type might get mutated when is exposed to the render, I would advise against using it like that.

image
avatar
May 19th 2023

@pikax thanks for the tip. But since props.list is a valid construct, the bug still exists. The issue should be reopened, right?

avatar
May 19th 2023

@rodrigocfd your issue is not the same issue on this issue, you can open another issue if you think your usage is valid.

The issue is not present inside the setup:

<script setup lang="ts" generic="T">
const props = withDefaults(
  defineProps<{
    value?: T | null;
    list: T[];
  }>(),
  {
    value: null,
  }
);

//no error here
for (const it of props.list) {
  console.log(it);
}
</script>

<template>
  <select>
    <option v-for="item of props.list"></option>
  </select>
</template>
image

A fix might be possible by shortcuting the types resolve on the props, feel free to PR.

avatar
May 31st 2023

Hello, I have vue version 3.3.4 and upon using the generic type within the defineProps with defaults value managad with the new updates to make it work but when I come to build I encounter the below error with the defineEmits when I am emitting a generic type back

error TS5088: The inferred type of 'default' references a type with a cyclic structure which cannot be trivially serialized. A type annotation is necessary.

Tried <script setup lang="ts" generic="I extends any"> and also adding "vueCompilerOptions": { "jsxTemplates": true, "experimentalRfc436": true }

but nothing is removing this error, anyone has any idea how this can be solved?