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

defineComponent cross-property generics usage is broadened on usage #3782

Closed
crutchcorn opened this issue Dec 6, 2023 · 4 comments
Closed
Labels

Comments

@crutchcorn
Copy link

I have a .vue file that accepts generic types like so:

<!-- Test.vue -->
<script setup lang="ts" generic="TStr extends 'one' | 'two', TNum extends TStr extends 'one' ? 1 : 2">
const props = defineProps<{
  str: TStr,
  num: TNum,
  numFn: (num: TNum) => TNum
}>()
</script>

<template>
  <div>
    <p>str: {{ props.str }}</p>
    <p>num: {{ props.num }}</p>
  </div>
</template>

And when using it in App.vue, it works just as expected to infer the values of numFn:

<!-- App.vue -->
<script setup lang="ts">
import Test from "./Test.vue"
</script>

<template>
  <!-- val is `1` -->
  <Test str="one" :num="1" :numFn="val => val" />
  <!-- val is `2` -->
  <Test str="two" :num="2" :numFn="val => val" />
</template>

However, when using defineComponent and generics inside like so:

// Test2.ts
import { defineComponent } from "vue";

export const Test2 = defineComponent(
  <TStr extends "one" | "two", TNum extends TStr extends "one" ? 1 : 2>(props: {
    str: TStr;
    num: TNum;
    numFn: (num: TNum) => TNum;
  }) => {
    return () => props.str;
  }
);

The type inferencing no longer works:

<script setup lang="ts">
import {Test2} from './Test2'
</script>

<template>
  <!-- val is `1 | 2` -->
  <Test2 str="one" :num="1" :numFn="val => val" />
  <!-- val is `1 | 2` -->
  <Test2 str="two" :num="2" :numFn="val => val" />
</template>

Link to Reproduction

https://github.com/crutchcorn/vue-define-component-ts-broadening-bug

https://stackblitz.com/github/crutchcorn/vue-define-component-ts-broadening-bug

@so1ve
Copy link
Member

so1ve commented Dec 6, 2023

Duplicate of #3745?

@crutchcorn
Copy link
Author

Maybe @so1ve? I think this is different behavior (altho probably linked) as there seems to be a unionization of possible values on top of what #3745 describes, even when they shouldn't technically be possible to union over

@finlayacourt
Copy link

A hack I’ve used to fix a similar issue:

Define an explicit return type, including Volar’s special __ctx attribute.

In your case,

import { type VNode, defineComponent } from "vue";

export const Test2 = defineComponent(
  <TStr extends "one" | "two", TNum extends TStr extends "one" ? 1 : 2>(props: {
    str: TStr;
    num: TNum;
    numFn: (num: TNum) => TNum;
  }) => {
    return () => props.str;
  }
) as <TStr extends "one" | "two", TNum extends TStr extends "one" ? 1 : 2>(props: {
  str: TStr;
  num: TNum;
  numFn: (num: TNum) => TNum;
}) => VNode & {
  __ctx?: {
    props: {
      str: TStr;
      num: TNum;
      numFn: (num: TNum) => TNum;
    };
  };
};

By changing __ctx, you can add type inference for emits and slots too.

@johnsoncodehk
Copy link
Member

I added a test case and found that this issue should have been solved in Vue 3.4, if you are using <= 3.3 please make sure to upgrade.

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

No branches or pull requests

4 participants