Skip to content

Spread syntax on iterables unsupported within <script lang="ts"> blocks in Vue files when running tests #445

Open
@jessevanassen

Description

@jessevanassen

Using the spread syntax to convert a non-array iterable (like a Map or Set) to an array doesn't work within a <script lang="ts"> block within a Vue file.
Depending on the TypeScript version, it will either produce the "RangeError: Invalid array length" error in TypeScript <= 4.1 (which is the default for @vue/cli projects) or an empty array when using a more recent version.

Reproduction

I created an empty project with the @vue/cli, and enabled Vue 3, TypeScript and Jest support.

/src/example.vue:

<script lang="ts">
import { defineComponent, h } from 'vue';

export default defineComponent({
	setup: () => ({
		values: [...new Set([1, 2, 3])],
	}),
	render: () => h('div'),
})
</script>

/test/unit/example.spec.ts:

import { mount } from '@vue/test-utils';
import Example from '../../src/example.vue';

test('Using a spread iterable from a Vue file', () => {
	const { vm } = mount(Example);
	expect(vm.values).toEqual([1, 2, 3]);
});

Running the test fails, either on a runtime error with an older TypeScript version, or on the assertion on newer TypeScript versions.

Full reproduction in reproduction.zip


The problem is caused by the TypeScript compiler running in ES5 mode for <script lang="ts"> blocks. This produces TypeScript code that is unaware of iterables and causes the seem behavior.
The same behavior can be reproduced in the TypeScript playground by targeting ES5.

Possible workaround

Next to targeting ES5 mode in lib/utils.js, also enable the downlevelIteration property, so iterables are properly spreadable:

diff --git a/packages/vue3-jest/lib/utils.js b/packages/vue3-jest/lib/utils.js
index 8ac12ee..f16b7b4 100644
--- a/packages/vue3-jest/lib/utils.js
+++ b/packages/vue3-jest/lib/utils.js
@@ -83,7 +83,7 @@ const getTsJestConfig = function getTsJestConfig(config) {
   const tsConfig = configSet.typescript || configSet.parsedTsConfig
   // Force es5 to prevent const vue_1 = require('vue') from conflicting
   return {
-    compilerOptions: { ...tsConfig.options, target: 'es5', module: 'commonjs' }
+    compilerOptions: { ...tsConfig.options, target: 'es5', module: 'commonjs', downlevelIteration: true }
   }
 }

However, that still leaves the underlying issue that <script lang="ts"> blocks are compiled to ES5, which produces huge amounts of code for constructs like async / await, the above-mentioned spreads, etc. which shouldn't be needed when targeting modern node versions.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions