Skip to content

Commit 0de5b33

Browse files
fix(vue): improve types for Vue Stencil components (#630)
* fix(vue): improve types for Vue Stencil components * prettier * debug test * bump test timeout * fix test * debug * add back vitest config * clean up * remove console.log
1 parent 1416ddc commit 0de5b33

18 files changed

+1498
-430
lines changed

example-project/component-library-vue/src/index.ts

+22-22
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable */
22
/* tslint:disable */
33
/* auto-generated vue proxies */
4-
import { defineContainer, defineStencilSSRComponent } from '@stencil/vue-output-target/runtime';
4+
import { defineContainer, defineStencilSSRComponent, type StencilVueComponent } from '@stencil/vue-output-target/runtime';
55

66
import type { JSX } from 'component-library';
77

@@ -19,7 +19,7 @@ import { defineCustomElement as defineMyToggle } from 'component-library/compone
1919
import { defineCustomElement as defineMyToggleContent } from 'component-library/components/my-toggle-content.js';
2020

2121

22-
export const MyButton = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.MyButton>('my-button', defineMyButton, [
22+
export const MyButton: StencilVueComponent<JSX.MyButton> = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.MyButton>('my-button', defineMyButton, [
2323
'color',
2424
'buttonType',
2525
'disabled',
@@ -38,7 +38,7 @@ export const MyButton = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.My
3838
], [
3939
'myFocus',
4040
'myBlur'
41-
]) : defineStencilSSRComponent({
41+
]) : defineStencilSSRComponent<JSX.MyButton>({
4242
tagName: 'my-button',
4343
hydrateModule: import('component-library/hydrate'),
4444
props: {
@@ -61,7 +61,7 @@ export const MyButton = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.My
6161
});
6262

6363

64-
export const MyCheckbox = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.MyCheckbox, JSX.MyCheckbox["checked"]>('my-checkbox', defineMyCheckbox, [
64+
export const MyCheckbox: StencilVueComponent<JSX.MyCheckbox, JSX.MyCheckbox["checked"]> = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.MyCheckbox, JSX.MyCheckbox["checked"]>('my-checkbox', defineMyCheckbox, [
6565
'color',
6666
'name',
6767
'checked',
@@ -79,7 +79,7 @@ export const MyCheckbox = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.
7979
'ionFocus',
8080
'ionBlur'
8181
],
82-
'checked', 'myChange') : defineStencilSSRComponent({
82+
'checked', 'myChange') : defineStencilSSRComponent<JSX.MyCheckbox, JSX.MyCheckbox["checked"]>({
8383
tagName: 'my-checkbox',
8484
hydrateModule: import('component-library/hydrate'),
8585
props: {
@@ -98,7 +98,7 @@ export const MyCheckbox = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.
9898
});
9999

100100

101-
export const MyComponent = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.MyComponent>('my-component', defineMyComponent, [
101+
export const MyComponent: StencilVueComponent<JSX.MyComponent> = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.MyComponent>('my-component', defineMyComponent, [
102102
'first',
103103
'middle',
104104
'last',
@@ -110,7 +110,7 @@ export const MyComponent = /*@__PURE__*/ globalThis.window ? defineContainer<JSX
110110
], [
111111
'myCustomEvent',
112112
'myCustomNestedEvent'
113-
]) : defineStencilSSRComponent({
113+
]) : defineStencilSSRComponent<JSX.MyComponent>({
114114
tagName: 'my-component',
115115
hydrateModule: import('component-library/hydrate'),
116116
props: {
@@ -125,7 +125,7 @@ export const MyComponent = /*@__PURE__*/ globalThis.window ? defineContainer<JSX
125125
});
126126

127127

128-
export const MyInput = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.MyInput, JSX.MyInput["value"]>('my-input', defineMyInput, [
128+
export const MyInput: StencilVueComponent<JSX.MyInput, JSX.MyInput["value"]> = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.MyInput, JSX.MyInput["value"]>('my-input', defineMyInput, [
129129
'color',
130130
'accept',
131131
'autocapitalize',
@@ -162,7 +162,7 @@ export const MyInput = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.MyI
162162
'myBlur',
163163
'myFocus'
164164
],
165-
'value', 'myChange') : defineStencilSSRComponent({
165+
'value', 'myChange') : defineStencilSSRComponent<JSX.MyInput, JSX.MyInput["value"]>({
166166
tagName: 'my-input',
167167
hydrateModule: import('component-library/hydrate'),
168168
props: {
@@ -199,7 +199,7 @@ export const MyInput = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.MyI
199199
});
200200

201201

202-
export const MyList = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.MyList>('my-list', defineMyList) : defineStencilSSRComponent({
202+
export const MyList: StencilVueComponent<JSX.MyList> = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.MyList>('my-list', defineMyList) : defineStencilSSRComponent<JSX.MyList>({
203203
tagName: 'my-list',
204204
hydrateModule: import('component-library/hydrate'),
205205
props: {
@@ -208,7 +208,7 @@ export const MyList = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.MyLi
208208
});
209209

210210

211-
export const MyListItem = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.MyListItem>('my-list-item', defineMyListItem) : defineStencilSSRComponent({
211+
export const MyListItem: StencilVueComponent<JSX.MyListItem> = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.MyListItem>('my-list-item', defineMyListItem) : defineStencilSSRComponent<JSX.MyListItem>({
212212
tagName: 'my-list-item',
213213
hydrateModule: import('component-library/hydrate'),
214214
props: {
@@ -217,7 +217,7 @@ export const MyListItem = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.
217217
});
218218

219219

220-
export const MyPopover = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.MyPopover>('my-popover', defineMyPopover, [
220+
export const MyPopover: StencilVueComponent<JSX.MyPopover> = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.MyPopover>('my-popover', defineMyPopover, [
221221
'component',
222222
'componentProps',
223223
'keyboardClose',
@@ -236,7 +236,7 @@ export const MyPopover = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.M
236236
'myPopoverWillPresent',
237237
'myPopoverWillDismiss',
238238
'myPopoverDidDismiss'
239-
]) : defineStencilSSRComponent({
239+
]) : defineStencilSSRComponent<JSX.MyPopover>({
240240
tagName: 'my-popover',
241241
hydrateModule: import('component-library/hydrate'),
242242
props: {
@@ -255,7 +255,7 @@ export const MyPopover = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.M
255255
});
256256

257257

258-
export const MyRadio = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.MyRadio>('my-radio', defineMyRadio, [
258+
export const MyRadio: StencilVueComponent<JSX.MyRadio> = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.MyRadio>('my-radio', defineMyRadio, [
259259
'color',
260260
'name',
261261
'disabled',
@@ -269,7 +269,7 @@ export const MyRadio = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.MyR
269269
'myFocus',
270270
'myBlur',
271271
'mySelect'
272-
]) : defineStencilSSRComponent({
272+
]) : defineStencilSSRComponent<JSX.MyRadio>({
273273
tagName: 'my-radio',
274274
hydrateModule: import('component-library/hydrate'),
275275
props: {
@@ -284,15 +284,15 @@ export const MyRadio = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.MyR
284284
});
285285

286286

287-
export const MyRadioGroup = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.MyRadioGroup, JSX.MyRadioGroup["value"]>('my-radio-group', defineMyRadioGroup, [
287+
export const MyRadioGroup: StencilVueComponent<JSX.MyRadioGroup, JSX.MyRadioGroup["value"]> = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.MyRadioGroup, JSX.MyRadioGroup["value"]>('my-radio-group', defineMyRadioGroup, [
288288
'allowEmptySelection',
289289
'name',
290290
'value',
291291
'myChange'
292292
], [
293293
'myChange'
294294
],
295-
'value', 'myChange') : defineStencilSSRComponent({
295+
'value', 'myChange') : defineStencilSSRComponent<JSX.MyRadioGroup, JSX.MyRadioGroup["value"]>({
296296
tagName: 'my-radio-group',
297297
hydrateModule: import('component-library/hydrate'),
298298
props: {
@@ -303,7 +303,7 @@ export const MyRadioGroup = /*@__PURE__*/ globalThis.window ? defineContainer<JS
303303
});
304304

305305

306-
export const MyRange = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.MyRange, JSX.MyRange["value"]>('my-range', defineMyRange, [
306+
export const MyRange: StencilVueComponent<JSX.MyRange, JSX.MyRange["value"]> = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.MyRange, JSX.MyRange["value"]>('my-range', defineMyRange, [
307307
'color',
308308
'debounce',
309309
'name',
@@ -326,7 +326,7 @@ export const MyRange = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.MyR
326326
'myFocus',
327327
'myBlur'
328328
],
329-
'value', 'myChange') : defineStencilSSRComponent({
329+
'value', 'myChange') : defineStencilSSRComponent<JSX.MyRange, JSX.MyRange["value"]>({
330330
tagName: 'my-range',
331331
hydrateModule: import('component-library/hydrate'),
332332
props: {
@@ -350,7 +350,7 @@ export const MyRange = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.MyR
350350
});
351351

352352

353-
export const MyToggle = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.MyToggle>('my-toggle', defineMyToggle) : defineStencilSSRComponent({
353+
export const MyToggle: StencilVueComponent<JSX.MyToggle> = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.MyToggle>('my-toggle', defineMyToggle) : defineStencilSSRComponent<JSX.MyToggle>({
354354
tagName: 'my-toggle',
355355
hydrateModule: import('component-library/hydrate'),
356356
props: {
@@ -359,9 +359,9 @@ export const MyToggle = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.My
359359
});
360360

361361

362-
export const MyToggleContent = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.MyToggleContent>('my-toggle-content', defineMyToggleContent, [
362+
export const MyToggleContent: StencilVueComponent<JSX.MyToggleContent> = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.MyToggleContent>('my-toggle-content', defineMyToggleContent, [
363363
'visible'
364-
]) : defineStencilSSRComponent({
364+
]) : defineStencilSSRComponent<JSX.MyToggleContent>({
365365
tagName: 'my-toggle-content',
366366
hydrateModule: import('component-library/hydrate'),
367367
props: {
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Vue 3 + TypeScript + Vite
2+
3+
This project is used to verify that the Vue Stencil Ouput Target propagates types correctly.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"name": "vue-app-broken",
3+
"private": true,
4+
"version": "0.0.0",
5+
"type": "module",
6+
"scripts": {
7+
"build.app": "vue-tsc --noEmit -p ./tsconfig.json",
8+
"test": "vitest --run"
9+
},
10+
"dependencies": {
11+
"component-library-vue": "workspace:*",
12+
"vue": "^3.5.13"
13+
},
14+
"devDependencies": {
15+
"vue-tsc": "^2.2.8",
16+
"vitest": "^3.0.8"
17+
}
18+
}
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<script setup lang="ts">
2+
import { MyComponent, MyButton } from 'component-library-vue'
3+
</script>
4+
5+
<template>
6+
<MyComponent
7+
kidsNames="John"
8+
/>
9+
<my-button fill="ups">Button</my-button>
10+
</template>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/// <reference types="vite/client" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import cp from 'node:child_process'
2+
import path from 'node:path'
3+
import url from 'node:url'
4+
5+
import { describe, it, expect } from 'vitest'
6+
7+
const __dirname = path.dirname(url.fileURLToPath(import.meta.url))
8+
9+
describe('type check', () => {
10+
it('should fail type check', async () => {
11+
const result = await new Promise((resolve, rejects) => {
12+
let stdout = ''
13+
const child = cp.exec(`npm run build.app`, {
14+
cwd: path.resolve(__dirname, '..'),
15+
})
16+
child.stdout?.on('data', (data) => {
17+
stdout += data
18+
})
19+
child.on('exit', (code) => {
20+
if (code !== 2) {
21+
return rejects(new Error(`Command failed with exit code ${code}`))
22+
}
23+
resolve(stdout)
24+
})
25+
});
26+
27+
expect(result).toContain(
28+
`App.vue(7,5): error TS2322: Type 'string' is not assignable to type 'string[]'.`
29+
)
30+
expect(result).toContain(
31+
`App.vue(9,14): error TS2322: Type '"ups"' is not assignable to type '"clear" | "outline" | "solid" | "default" | undefined'.`
32+
)
33+
})
34+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"compilerOptions": {
3+
"target": "ES2020",
4+
"useDefineForClassFields": true,
5+
"module": "ESNext",
6+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
7+
"skipLibCheck": true,
8+
9+
/* Bundler mode */
10+
"moduleResolution": "bundler",
11+
"allowImportingTsExtensions": true,
12+
"isolatedModules": true,
13+
"moduleDetection": "force",
14+
"noEmit": true,
15+
"jsx": "preserve",
16+
17+
/* Linting */
18+
"strict": true,
19+
"noEmitOnError": true,
20+
"noUnusedLocals": true,
21+
"noUnusedParameters": true,
22+
"noFallthroughCasesInSwitch": true
23+
},
24+
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { defineConfig } from 'vitest/config'
2+
3+
export default defineConfig({
4+
test: {
5+
testTimeout: 30 * 1000, // 30 seconds
6+
},
7+
})

example-project/vue-app/package.json

+15-14
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,27 @@
55
"type": "module",
66
"scripts": {
77
"dev": "vite",
8-
"build": "vue-tsc -b && vite build",
8+
"build": "vue-tsc --noEmit -p ./tsconfig.app.json && vite build",
99
"preview": "vite preview",
1010
"test": "wdio run ./wdio.conf.ts"
1111
},
1212
"dependencies": {
1313
"component-library-vue": "workspace:*",
14-
"vue": "^3.5.10"
14+
"vue": "^3.5.13"
1515
},
1616
"devDependencies": {
17-
"@vitejs/plugin-vue": "^5.1.4",
18-
"@wdio/cli": "^9.4.5",
19-
"@wdio/globals": "^9.2.1",
20-
"@wdio/mocha-framework": "^9.1.3",
21-
"@wdio/spec-reporter": "^9.1.3",
22-
"expect-webdriverio": "^5.0.3",
23-
"tsx": "^4.19.1",
24-
"typescript": "^5.5.3",
25-
"vite": "^5.4.8",
26-
"vue-tsc": "^2.1.10",
27-
"wdio-vite-service": "^1.0.9",
28-
"webdriverio": "^9.1.5"
17+
"@vitejs/plugin-vue": "^5.2.1",
18+
"@wdio/cli": "^9.11.0",
19+
"@wdio/globals": "^9.11.0",
20+
"@wdio/mocha-framework": "^9.11.0",
21+
"@wdio/spec-reporter": "^9.11.0",
22+
"expect-webdriverio": "^5.1.0",
23+
"tsx": "^4.19.3",
24+
"typescript": "^5.8.2",
25+
"vite": "^6.2.1",
26+
"vite-plugin-checker": "^0.9.0",
27+
"vue-tsc": "^2.2.8",
28+
"wdio-vite-service": "^2.0.0",
29+
"webdriverio": "^9.11.0"
2930
}
3031
}

example-project/vue-app/tsconfig.app.json

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
/* Linting */
1818
"strict": true,
19+
"noEmitOnError": true,
1920
"noUnusedLocals": true,
2021
"noUnusedParameters": true,
2122
"noFallthroughCasesInSwitch": true

packages/vue/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
"@vue/shared": "^3.5.12",
6464
"rimraf": "^5.0.0",
6565
"rollup": "^4.14.3",
66-
"typescript": "~5.7.0"
66+
"typescript": "~5.7.0",
67+
"vitest": "^3.0.8"
6768
}
6869
}

0 commit comments

Comments
 (0)