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

Generic slot props in loops with discriminated union props definition breaks slot prop type #12012

Open
cathrinevaage opened this issue Sep 23, 2024 · 7 comments

Comments

@cathrinevaage
Copy link
Contributor

cathrinevaage commented Sep 23, 2024

Vue version

^5.0

Link to minimal reproduction

https://play.vuejs.org/#eNqVVNtq20AQ/ZVBL7EhkSlpoQgl0Eso6UMaGpc+ZENR5ZGzqTQrdleug6t/78yu5Zhiu8mTds/czpyZ1Sp517bposMkS3JXWt16UATg0HdtONUFzc9U4p1KFJ0r0k1rrIcPhr+E5L9rf/+NtKFra1oHlTUNHKWTPXYpdaQon8RanI8vHpu2LjzyDSDfl9mZBk9aPsKEHfPJVlRyzPxKQ5Wepw/OEDezklwqKTmZrtF+aT0n4h4yCBaxFXVtfn8OmLcdHg94eY/lrx34g1sKppJriw7tAlWysfnCztFH88XNFS75vDE2ZtbV7H3A+BWdqTvhGN3edzRj2lt+ge1lEF/TfOoulh7JDU0JUfHsg79KWGbRcV/rT3RP09chTlHPKh6Y2tOCbPbjuevxCQmtLm9q43fvx5bDekEUzbDShKF8PpJKf2AVdkCgDKqidii0/zWIEoKPz0djycNr4Txo3hYHZ3AbJNCzDF4FpYY4tP7xyvjL0O9HKa1FtQyOKmOYT3/3gqXdaicKnoXyLFP4ilKCLk4cezC6CvSYdDCEhACriKb/YQh90CDfK+iOt3JI++eMGWAegxiZAq8z0szxEERW6pqfaGMvXG17ilF6USCD6e0d69+vZxS9hMLai4Giqz2M5L2z+3pxJZhjw+T6cQYFPQ5JDg1HZB4kr4xl1iPJNAZNkc8wkTCn9ZgitutX82OBVh4eS3WavknfJv1fZJPSBA==

Steps to reproduce

  1. In a component, define a generic type, and use it as the array type for a prop.
    E.g.
<script
  setup
  lang="ts"
  generic="T extends { id: number }"
>
defineProps<{
  items: T[],
}>()
</script>
  1. Define a default slot, with the generic as a slot prop.
    E.g.
defineSlots<{
  default (props: {
    item: T,
  }): any
}>()
  1. In a component use defineSlots to define props with a discriminated union.
    E.g.
defineProps<(
  | { someProp: false }
  | { someProp: true }
)>()
  1. Declare a variable that fits the prop of the first component, with an additional property.
    E.g.
const items = [{
  id: 1,
  somePropertyNotInThePropDefinition: 'foo'
}]
  1. Use the first component in the template, with the variable as the prop, and use the v-slot directive so that the slot prop is available. Inside the slot body, access the additional property.
    E.g.
<ComponentWithGenericSlot
  :items="items"
  v-slot="{ item }"
>
  {{ item.somePropertyNotInThePropDefinition }}
</ComponentWithGenericSlot>

What is expected?

The additional property should be typed correctly with the additional property in the slot prop.

What is actually happening?

The additional property is not recognized. The type of the slot prop is the type of the generic, and nothing else.

System Info

System:
    OS: macOS 15.0
    CPU: (8) arm64 Apple M1
    Memory: 50.31 MB / 16.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 20.17.0 - /usr/local/bin/node
    Yarn: 1.22.19 - /opt/homebrew/bin/yarn
    npm: 10.8.2 - /usr/local/bin/npm
    pnpm: 7.33.7 - /opt/homebrew/bin/pnpm
    bun: 1.0.14 - /opt/homebrew/bin/bun
  Browsers:
    Safari: 18.0
  npmPackages:
    vue: ^3.5.0 => 3.5.8

Any additional comments?

This started with Vue 5.5. Changing the typescript or vue-tsc versions doesn't affect this.

Curiously, in my local project, when using vue-tsc, wrapping the defineProp in an empty withDefaults was required to produce the described behaviour. This is not the case in the play.vuejs.org editor.

@KazariEX
Copy link
Contributor

Set vueCompilerOptions.target to 3.5.

@edison1105 edison1105 closed this as not planned Won't fix, can't repro, duplicate, stale Sep 24, 2024
@cathrinevaage
Copy link
Contributor Author

@edison1105 adding vueCompilerOptions: { target: 3.5 } to my local project does not fix the issue like it does in the playground. Should i open a new issue with a repro repo, or should I post it here?

@edison1105
Copy link
Member

@cathrinevaage
try to use the latest version of vue-tsc

@cathrinevaage
Copy link
Contributor Author

@edison1105
I'm running 2.1.6, so as far as I can tell, I already am.
I uploaded a repro here https://github.com/cathrinevaage/vue-generic-slot-props-issue-repro

@edison1105 edison1105 reopened this Sep 24, 2024
@KazariEX
Copy link
Contributor

The union props type declaration has break the defineComponent overload.

image

@KazariEX
Copy link
Contributor

KazariEX commented Sep 26, 2024

Now it works, but I don't know if all of this is worth it 😇

image

/// <reference types=".vue-global-types/vue_3.5_false.d.ts" />

type __VLS_Props = {
		foo: 'checkbox' | 'radio';
	} | {
		foo?: boolean;
        bar: string;
	} | {
        baz: number | undefined;
    };

let props!:  __VLS_TypePropsToOption<__VLS_Props>;

type __VLS_TypePropsToOption<T> = __VLS_PrettifyUnion<__VLS_MergeUnion<T>> extends infer U ? {
	[K in keyof U]-?: {
        type: import('vue').PropType<U[K]>,
        required: {} extends Pick<U, K> ? false : true
    }
} : never;

type __VLS_MergeUnion<T, U = T> = T extends any
    ? { [V in __VLS_PublicOptionalKeys<U>]?: __VLS_InferPropType<T, U, V> }
    & { [V in __VLS_PublicRequiredKeys<U>]: __VLS_InferPropType<T, U, V> }
    : never;

type __VLS_PublicRequiredKeys<T, K = __VLS_UnionKeys<T>> = Exclude<K, __VLS_PublicOptionalKeys<T>>;
type __VLS_PublicOptionalKeys<T, K = __VLS_UnionKeys<T>> = T extends any
    ? K extends keyof T
        ? {} extends Pick<T, K>
            ? K
            : never
        : never
    : never;

type __VLS_UnionKeys<T> = T extends any ? keyof T : never;

type __VLS_InferPropType<T, U, K> = K extends keyof T ? T[K] : K extends keyof U ? U[K] : never;

type __VLS_PrettifyUnion<T> = {
    [K in __VLS_UnionKeys<T> as {} extends Pick<T, K> ? never : K]: T[K]
} & {
    [K in __VLS_UnionKeys<T> as {} extends Pick<T, K> ? K : never]?: T[K]
};

@KazariEX
Copy link
Contributor

KazariEX commented Nov 2, 2024

Please define a generic type used by union props as a workaround:

<script setup lang="ts" generic="T extends {
    foo: string
} | {
    foo?: number
    bar: boolean
} | {
    baz: boolean
}">
defineProps<T>();
</script>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants