core: `withDefaults` shows error when using `generic`

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

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Reactions: 16
  • Comments: 25 (9 by maintainers)

Commits related to this issue

Most upvoted comments

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?

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

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

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>

@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.

@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

@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

<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.

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

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

+1