script-setup: Types need to be imported separately with `import type ....`
Version
3.0.5
Reproduction link
https://github.com/JSerFeng/bug/blob/master/src/App.vue
Steps to reproduce
What is expected?
nothing happens
What is actually happening?
Uncaught SyntaxError: The requested module '/node_modules/.vite/vue.js?v=6cc5afeb' does not provide an export named 'Ref'
It's a Bug in the compiler that doesn't differentiate between type exports and runtime exports.
As a workaround, you can use import type { Ref } from 'vue'
to import types explicitly as types.
Is it related to vite? https://github.com/vitejs/vite/issues/1661
Don't think so, that issue revolved around runtime exports (unref
, openBlock
).
There were also comments about the problem with Type exports in the RFC comments that found the same solution/workaround:
https://github.com/vuejs/rfcs/pull/227#issuecomment-766427503
Oh, the code it generates looks like this:
import {Ref} from 'vue'
export default {
expose: [],
setup(__props) {
return { Ref }
}
}
But I can't find any way other than list all types through enumeration, or we can consider imports beginning with capital letters as types, but exclude such things as Teleport/ErrorCodes
.
Yeah, don't think that would be a reliable heuristic. I don't think the compiler can reliably determine wether or not an import is a Type or a runtime object, so in theory we would have to clean up the imports with Ts somehow before generating the code.
No idea if something like this is even feasable, not really familiar enough with compiler details.
Should we consider this an enhancement rather than a bug as import type
works for now, and the RFC is still in flux?
Should we consider this an enhancement rather than a bug as import type works for now, and the RFC is still in flux?
Agree
This workaround breaks eslint-prettier, so weird.
It's a Bug in the compiler that doesn't differentiate between type exports and runtime exports.
As a workaround, you can use
import type { Ref } from 'vue'
to import types explicitly as types.
@LinusBorg
The problem is I didn't install prettier as dependency.
(I thought that eslint-plugin-prettier
has the dependency)
Everything is okay right now, sorry for that.
I rethinked this with https://github.com/vuejs/vue-next/issues/3249. Why do we need to expose the imports from vue
? maybe it was a mistake? I know that build-in components must be exposed, like Teleport
:
<script setup>
import { Teleport } from 'vue'
</script>
output:
export default {
expose: [],
setup() {
return { Teleport }
}
}
So we only need a whitelist: Teleport
, Transition
, TransitionGroup
, KeepAlive
, Suspense
For Types the import type solution worked for me. But I cant get interfaces working. I always get the warning, that the interface is not exported.
"export 'Consultation' was not found in '@/libs/shared/interfaces/consultation'
I think compiler has no a perfect way to solve this problem, but IDE already report error for this use case, so maybe we don't need to do anything in compiler side.
This impacts types, which you can work-around by using import type
; and const enums, which you can't work around in a practical way (both import
and import type
create errors, in different places).
One solution to this problem could be to check what is actually used by the template and not export identifiers that it doesn't use.
- Not sure if that would fully work with HMR?
- It's more work as it would require analyzing the globals of every expression in template, but at least it's a local solution to the problem (i.e. no need to do cross-files type analysis to figure out what is a type).
- It only partially solves the const enum problem: now you can import them and use them in code, but you still can't use them in template.
I believe none of this is an issue when building for production and the template becomes a lambda inside the setup code, which simply has local scope automatically captured.
That's actually great and the best solution, the only issue is that the other way is used in dev to enable HMR / exposing state to dev tools. I wonder if we could figure out a way to do those things with the lambda design instead... (I don't have an idea just yet 🤔)