-
-
Notifications
You must be signed in to change notification settings - Fork 8.4k
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
Conditional properties through discriminated unions and intersections in TypeScript #8952
Comments
Added another simple example with fully "exclusive" props that should be covered as well and does not work (yet). |
I ran into this many times, huge +1. |
Typescript allows me to use Conditional Types, but Vue doesn't allow me to do this |
C'mon Vue and TS NEED FULL SYNERGY. |
Hi guys, Any update here? @sxzz if you can please follow up on this issue. |
This is a type restriction of defineComponent, as a current solution you can use generic. This will bypass defineComponent and define the component as a functional component. <script setup lang="ts" generic="T">
interface CommonProps {
size?: 'xl' | 'l' | 'm' | 's' | 'xs'
}
// ...
</script> |
Unfortunately vue-test-utils does not support generic components with Typescript |
@johnsoncodehk while props with a discriminator work now in the playground (e.g. |
Any updates on the plans for this? |
It's mostly because of how Typescript is designed due to its structural type system and how Typescript does checks with unions. So in your example, if we simplify it for Typescript, it would be smth like this: type Props = { one: string } | { other: number };
const Component = (props: Props) => {};
Component({ one: '123', other: 1 }) // no errors So when you pass some object, it will be checked for each union member separately if it satisfies them. The current object satisfies both of them, so there are no errors. type Props = { one: string } | { other: number };
const obj = {
one: 'sad',
other: 1
} satisfies Props; // no error So it's mostly not a problem of vue or its typings, it's how Typescript works with unions. The solution here would be to use discriminated unions (have the same property with different values in union type) or you can manually create a type that will exclude other properties by assigning a type PropsWithNever = { one: string; other?: never } | { one?: never; other: number };
const objNever1 = { one: 'asd' } satisfies PropsWithNever;
const objNever2 = { other: 1 } satisfies PropsWithNever;
const objNever3 = {
one: 'asd',
other: 1
} satisfies PropsWithNever; // shows error To simplify usage of that I usually use these utilities: type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };
export type XOR<T, U> = T | U extends object
? (Without<T, U> & U) | (Without<U, T> & T)
: T | U;
type PropsWithXOR = XOR<{ one: string }, { other: number }>;
const objWithXOR = {
one: 'sad',
other: 1
} satisfies PropsWithXOR; // shows error You can check and play with example here I usually use XOR when I have common props and I can make P.S. BTW, I've found nice detailed explanation on this problem in typescript with solution: video |
@Sengulair nice idea while this works on a type level it currently is not working on a vue component see we will get Error: [@vue/compiler-sfc] Unresolvable type: TSConditionalType
src/Comp.vue
4 | type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };
5 |
6 | export type XOR<T, U> = T | U extends object
| ^^^^^^^^^^^^^^^^^^^^
7 | ? (Without<T, U> & U) | (Without<U, T> & T)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
8 | : T | U;
| ^^^^^^^^^
at $Re.error (https://play.vuejs.org/assets/index-CVeOR0Tn.js:187:397) |
What problem does this feature solve?
#7553
Reopening this because I believe the problem is not solved. It is still impossible to use coditional props.
Just try this after
npm create vue@latest
inside HelloWorld componentand then try to use it
Theoretically it should not allow us to use both. That is, there can't be color="white" appearance="text" only color="white" appearance="outline"
What does the proposed API look like?
The text was updated successfully, but these errors were encountered: