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

Support imported types in SFC macros #8083

Merged
merged 9 commits into from
Apr 15, 2023
Merged

Support imported types in SFC macros #8083

merged 9 commits into from
Apr 15, 2023

Conversation

yyx990803
Copy link
Member

@yyx990803 yyx990803 commented Apr 14, 2023

Summary

SFC macros that need to generate runtime options from types (i.e. defineProps and defineEmits) can now directly reference imported types.

Details

Relative Imports

The following will throw an error in <3.3, but will work after this PR:

<script setup lang="ts">
import type { Props } from './foo'
defineProps<Props>()
</script>

In addition, more complex types are also supported, e.g. you can do:

import type { Props } from './foo'
defineProps<Props & {
  additionalProp?: string
}>()

Do note that complex types support is AST-based (not using TS itself) and therefore not 100% comprehensive. For example, you cannot use conditional types for the props type itself:

import type { Props } from './foo'
// will throw a compiler error
defineProps<Props extends object ? Props : {}>()

Module / Path Imports

This feature requires TypeScript to be a peer dependency of vue.

When importing types from a non-relative source, e.g.:

import type { Props } from 'some-package'
defineProps<Props>()

Vue will use TypeScript's API to:

  1. Find closest tsconfig.json using ts.findConfigFile
  2. Load and parse the config for compilerOptions using ts.parseJsonConfigFileContent (this step is cached)
  3. Resolve the actually imported file with ts.resolveModuleName

This ensures the resolution is the same as user's configured TS environment, respecting the resolution and paths specified in tsconfig.json.

Failed Resolves

  • If a type reference used at the root level fails to resolve, it will be a hard error, since it prevents Vue from generating the correct list of runtime props.
  • If a type reference used in a nested property fails to resolve, it will silently fallback to unknown type. This is the current behavior in 3.2, so it's preserved to avoid breakage.

Performance Implications

This feature can potentially cause compiler-sfc to resolve, parse, crawl, and analyze multiple additional type files. To avoid performance overhead, the analyzed result of the same file referenced by multiple components will be cached (and invalidated by bundler plugins), and the type reference crawl is performed lazily only when necessary. The overall performance overhead should be negligible in theory, but we will need to gather some feedback from real world projects.

HMR

For this feature to work properly with HMR, it will require additional changes in @vitejs/plugin-vue and vue-loader to support HMR on changes from imported types.

@innocenzi
Copy link

Will this work for global .d.ts types? eg. you have some UserData interface in a .d.ts file somewhere, can you do this now?

defineProps<UserData>()

Would be super cool but my understanding is that the improved support is only for specifically imported types?

@yyx990803
Copy link
Member Author

Global types won't work - this can only work with explicitly imported types.

@innocenzi
Copy link

Could there be a way to feed a list of .d.ts files to the Vue compiler (eg. through the Vite plugin) so Vue can find types in these?

The use case is for back-end integrations. We generate types from non-TypeScript code (PHP in this example) into .d.ts files. These types can be used everywhere on the front-end, except as a generic to defineProps. Our workaround is to do something like:

defineProps<{
  user: App.Data.UserData
}>()

Instead of defineProps<App.Data.UserData>(). But in theory if Vue knows about these types somehow, this should be possible right?

@yyx990803 yyx990803 merged commit 760755f into main Apr 15, 2023
@yyx990803 yyx990803 deleted the sfc-external-types branch April 15, 2023 14:08
@yyx990803
Copy link
Member Author

@innocenzi yes, that would be technically possible. Although I'll probably do that in a separate PR.

@innocenzi
Copy link

That would be amazing. Thanks for this PR as well, super useful.

@laterdayi
Copy link

Looking forward to it, when will it be available

@yyx990803
Copy link
Member Author

@innocenzi global types supported via 4e028b9

@innocenzi
Copy link

@yyx990803 Thank you so much! ❤️

@nikuscs
Copy link

nikuscs commented Apr 16, 2023

Damn, amazing work! Thanks a ton!

@wenfangdu
Copy link

Currently complex types and type imports from other files are not supported. It is possible to support type imports in the future.

Time to update the doc.

@Hccake
Copy link

Hccake commented Apr 19, 2023

After using the 3.3.0 alpha version, an error is reported:

Unresolvable type reference or unsupported built-in utilility type

code like :

interface MyComponentProps  extends ComponentProps {
  someprop: string
}

const props = withDefaults(defineProps<MyComponentProps>(), {
  someprop: 'xxxx'
})

The above code works normally in version 3.2.x,ComponentProps import from third-party package

@edison1105
Copy link
Member

@Hccake Would you mind providing a runnable minimal reproduction?

@Hccake
Copy link

Hccake commented Apr 20, 2023

@Hccake Would you mind providing a runnable minimal reproduction?

@edison1105 Ok, the link is here: https://codesandbox.io/p/github/Hccake/vue3-typescript/master

If you use vue 3.2.x version code can run normally

@edison1105
Copy link
Member

edison1105 commented Apr 20, 2023

@Hccake Would you mind providing a runnable minimal reproduction?

@edison1105 Ok, the link is here: codesandbox.io/p/github/Hccake/vue3-typescript/master

If you use vue 3.2.x version code can run normally

import type {ButtonProps} from "ant-design-vue";
            // ButtonProps = Partial<ExtractPropTypes<ReturnType<typeof buttonProps>>>;

ExtractPropTypes is not supported
see #8104 (comment)

@yyx990803 The error caused the code to not run as well as 3.2.x.

@pangao66
Copy link

pangao66 commented Apr 20, 2023

@Hccake

<script setup lang="ts">
import inputProps from "ant-design-vue/es/input/inputProps";
defineProps(inputProps());
</script>

do like this without wait for 3.3

@Hccake
Copy link

Hccake commented Apr 20, 2023

@Hccake

<script setup lang="ts">
import inputProps from "ant-design-vue/es/input/inputProps";
defineProps(inputProps());
</script>

do like this without wait for 3.3

The problem is not how to implement it, but that this change will cause existing code to be unable to upgrade smoothly

@brolnickij
Copy link

brolnickij commented May 3, 2023

@yyx990803 should we expect this feature in vue@2.7? thank you!

sgfost added a commit to sgfost/comses.net that referenced this pull request May 4, 2023
using imported types with defineProps is currently unsupported but has
been fixed awaiting a release in vuejs/core#8083
sgfost added a commit to sgfost/comses.net that referenced this pull request May 9, 2023
using imported types with defineProps is currently unsupported but has
been fixed awaiting a release in vuejs/core#8083
sgfost added a commit to sgfost/comses.net that referenced this pull request May 9, 2023
using imported types with defineProps is currently unsupported but has
been fixed awaiting a release in vuejs/core#8083
sgfost added a commit to sgfost/comses.net that referenced this pull request Jun 1, 2023
using imported types with defineProps is currently unsupported but has
been fixed awaiting a release in vuejs/core#8083
@MaxHammermann
Copy link

@yyx990803 should we expect this feature in vue@2.7? thank you!

This would be very nice, although I couldn't think it will be.
Is there a place where I can look up what went into the compat build 2.7 and if there will be another version > 2.7.14?

@sxzz
Copy link
Member

sxzz commented Jun 21, 2023

You can try betterDefine of Vue Macros.

sgfost added a commit to sgfost/comses.net that referenced this pull request Jun 27, 2023
using imported types with defineProps is currently unsupported but has
been fixed awaiting a release in vuejs/core#8083
sgfost added a commit to sgfost/comses.net that referenced this pull request Jun 27, 2023
using imported types with defineProps is currently unsupported but has
been fixed awaiting a release in vuejs/core#8083
@aayler
Copy link

aayler commented Jun 27, 2023

greetings!

would really appreciate a confirmation:
does this feature support import via alias in vite, ie, @?

when we use something like import { PageSlug } from '@/types/audio.interface'; vite generates:
[commonjs] [@vue/compiler-sfc] Failed to resolve import source "@/types/audio.interface".

while using relative path import { PageSlug } from '../../types/audio.interface'; produces no errors.

if this is the case, is there expectation for support of aliased paths?

thanks so much!

alee pushed a commit to sgfost/comses.net that referenced this pull request Jul 24, 2023
using imported types with defineProps is currently unsupported but has
been fixed awaiting a release in vuejs/core#8083
@awacode21
Copy link

Does it only support types? Or does it also support interfaces?

hwelsters pushed a commit to aimura09/comses.net that referenced this pull request Oct 23, 2023
using imported types with defineProps is currently unsupported but has
been fixed awaiting a release in vuejs/core#8083
@leeeisok
Copy link

I get this error (only during build) when importing a type with relative path eg:

'../types/myfile' ❌

when I use project root import, it resolves without errors

'@/types/myfile' ✅

I should note that this error arises when I import a file to use in defineProps() since we can do that now which is awesome 🙂

I fixed it with alias import, but I think it should be fixed for relative imports too

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

Successfully merging this pull request may close these issues.