`withDefaults` shows error when using `generic`
Vue version
3.3.2
Link to minimal reproduction
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
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,
});
@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.
Same error, this should be documented if it is intended. At least they don't show an example in the official documentation.
Same error, this should be documented if it is intended.
@pikax 's demo is TypeScript's feature. Not Vue's feature.
@an501920078 The problem you are having is not the same as the problem with pikax. It is recommended to file an issue separately.
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>
@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.
@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
@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
<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.
@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.
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.
@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
Even with casting
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
After I update to 3.3.2 same error occurred:
When I use interface instead of type there are error too:
@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
@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?
@pikax what am I doing wrong here?
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
@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.
@pikax thanks for the tip. But since props.list
is a valid construct, the bug still exists. The issue should be reopened, right?
@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>
A fix might be possible by shortcuting the types resolve on the props
, feel free to PR.
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?