Skip to content

fix(language-core): drop undefined from type of optional prop with default in template #5339

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

Merged
merged 6 commits into from
Apr 27, 2025

Conversation

Dylancyclone
Copy link
Contributor

Currently, Optional prop with default may incorrectly be undefined in template when exactOptionalPropertyTypes and strictNullChecks are true: #5338

Here is a Playground link to explore the change: Vue SFC Playground
(The same thing can be seen when compiling locally, and I can provide a full repository if it would help. I don't know how to apply the patch to the VSCode extension/IDE, so I've only been able to test it when running vue-tsc)

While looking into the issue I reported above, I noticed that the __VLS_WithDefaults type in language-core/lib/codegen/localTypes.ts would still keep a prop as possibly undefined even if a default value was provided. This would be visible when trying to access that prop in the template, where a typescript error would be thrown because it believes the prop could possibly be undefined. The error would not show up if accessing the prop through the props object, only when referencing it directly. See the issue for more information

This is my first time diving into internal Vue code, so please let me know if there's anything I should do differently!

Copy link

pkg-pr-new bot commented Apr 25, 2025

Open in StackBlitz

vue-component-meta

npm i https://pkg.pr.new/vuejs/language-tools/vue-component-meta@5339

vue-component-type-helpers

npm i https://pkg.pr.new/vuejs/language-tools/vue-component-type-helpers@5339

@vue/language-core

npm i https://pkg.pr.new/vuejs/language-tools/@vue/language-core@5339

@vue/language-plugin-pug

npm i https://pkg.pr.new/vuejs/language-tools/@vue/language-plugin-pug@5339

@vue/language-server

npm i https://pkg.pr.new/vuejs/language-tools/@vue/language-server@5339

@vue/language-service

npm i https://pkg.pr.new/vuejs/language-tools/@vue/language-service@5339

@vue/typescript-plugin

npm i https://pkg.pr.new/vuejs/language-tools/@vue/typescript-plugin@5339

vue-tsc

npm i https://pkg.pr.new/vuejs/language-tools/vue-tsc@5339

commit: c60d314

@KazariEX
Copy link
Member

KazariEX commented Apr 26, 2025

I think the current handling of exactOptionalPropertyTypes in __VLS_TypePropsToOption seems to be not correctly.

() => compilerOptions.exactOptionalPropertyTypes ?
`
type __VLS_TypePropsToOption<T> = {
[K in keyof T]-?: {} extends Pick<T, K>
? { type: import('${vueCompilerOptions.lib}').PropType<T[K]> }
: { type: import('${vueCompilerOptions.lib}').PropType<T[K]>, required: true }
};
`.trimStart() :
`
type __VLS_NonUndefinedable<T> = T extends undefined ? never : T;
type __VLS_TypePropsToOption<T> = {
[K in keyof T]-?: {} extends Pick<T, K>
? { type: import('${vueCompilerOptions.lib}').PropType<__VLS_NonUndefinedable<T[K]>> }
: { type: import('${vueCompilerOptions.lib}').PropType<T[K]>, required: true }
};
`.trimStart()

It should be:

type __VLS_TypePropsToOption<T> = {
	[K in keyof T]-?: {} extends Pick<T, K>
		? { type: import('${vueCompilerOptions.lib}').PropType<Required<T>[K]> }
		: { type: import('${vueCompilerOptions.lib}').PropType<T[K]>, required: true }
};

So that we don't need to make any changes to __VLS_WithDefaults. What do you think?

@Dylancyclone
Copy link
Contributor Author

Dylancyclone commented Apr 26, 2025

Unfortunately, I don't think that will work, because it would mark optional props that don't have a default as not possibly undefined as well as props that have a default value. For example:

const props = withDefaults(
  defineProps<{
    stringWithDefault?: string;
    stringWithDefaultCanBeUndefined?: string | undefined
    optionalStringWithoutDefault?: string
  }>(),
  {
    stringWithDefault: 'defaultValue',
    stringWithDefaultCanBeUndefined: "defaultVal"
  }
);

image
The underlined type should possibly be undefined because if the key is not provided the type of the prop's value is undefined.
This is because even though optionalStringWithoutDefault cannot be set to an undefined value because of exactOptionalPropertyTypes, it can still be undefined in the case the key doesn't exist
Vue Playground link
Typescript Playground link

That's why I made my changes to __VLS_WithDefaults, since the current functionality is only wrong when there is a default value

Thank you for helping look into this!

@KazariEX
Copy link
Member

KazariEX commented Apr 27, 2025

It should be optional by default when there is no required: true option.

@KazariEX
Copy link
Member

I added test case so that you can intuitively see the correct behavior after modifications. Thanks for your contribution!

@KazariEX KazariEX changed the title Correctly type optional prop with default in __VLS_ctx fix(language-core): drop undefined from type of optional prop with default in template Apr 27, 2025
@KazariEX KazariEX merged commit a7b5649 into vuejs:master Apr 27, 2025
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants