Adding `generic=` to `<script setup>` makes it impossible to export types from SFC
Vue version
3.5.16
Link to minimal reproduction
https://github.com/alexchexes/vue-test-generic-export/blob/master/src/App.vue
Steps to reproduce
- Add
generic="T"attribute to the<script setup>(<script setup lang="ts" generic="T">) - Add a type export, e.g.
export type MyType = 123, within that script setup scope - Observe the
Modifiers cannot appear here. ts(1184)TypeScript error in VSCode - Remove
generic="T"and the error disappears, confirming the issue is related to thegeneric="T"attribute
Reproduction:
<script setup lang="ts" generic="T">
defineProps<{
items: string[]
selected: string
}>()
export type MyType = 123 // ← TS Error: Modifiers cannot appear here. ts(1184)
</script>
What is expected?
No error should occur when exporting types after adding generic="T" to <script setup>
What is actually happening?
A Modifiers cannot appear here. ts(1184) TypeScript error appears as soon as generic="T" is added to <script setup>, but no error occurs when it is removed
System Info
System:
OS: Windows 11 10.0.26100
CPU: (20) x64 12th Gen Intel(R) Core(TM) i7-12700H
Memory: 32.97 GB / 63.67 GB
Binaries:
Node: 22.11.0 - C:\Program Files\nodejs\node.EXE
Yarn: 1.22.22 - ~\AppData\Roaming\npm\yarn.CMD
npm: 10.9.0 - C:\Program Files\nodejs\npm.CMD
pnpm: 9.12.3 - ~\AppData\Roaming\npm\pnpm.CMD
Browsers:
Edge: Chromium (130.0.2849.80)
npmPackages:
vue: ^3.5.13 => 3.5.16
Any additional comments?
If this is a restriction of generic components, we should mention it in the generics section of the <script setup> docs
When we use generic, we should assume that all the code in the script block is within the scope of the function.
You can move the type declarations with export modifier to the top to avoid this situation.
You can move the type declarations with export modifier to the top to avoid this situation
Unlike the code in the example, in the real code the use case is to export the type of whatever emit() returns. I mean something like this (real code):
const emit = defineEmits<{
/** emitted in case of any error (usually only network errors occurs) */
'error': [error: unknown];
/** emitted after suggestion is selected, whether by clicking on a suggestion in the dropdown, by pressing "Enter" or by auto-selecting when `selectOnBlur=true` */
'select': [suggestion: DadataSuggestion, selectType: SelectType];
/** emitted after selected suggestion was enriched in case `enrichOnSelect` props is `true` */
'enriched': [suggestion: DadataSuggestion, diff: DeepPartial<DadataSuggestion> | null];
/** emitted if attemp to enrich selected suggestion failed (in case `enrichOnSelect` props is `true`) */
'enrichFail': [unrestricted_value: string];
/** emitted whenever input is focused */
'focus': [event: FocusEvent];
/** emitted whenever input looses focus */
'blur': [event: FocusEvent];
}>();
export type VueDadataEmits = typeof emit;
So we can pass emit to the composable and emit from there without using any any.
What is the recommended approach in this case?
Firstly, ensure that the type of the emit is independent of generic type parameters; Then it is best to use some type helpers to explicitly infer the type of emit in a non setup script block and export it.