Vue TSX Type Support
vue-tsx-support is a TypeScript support library designed to enable and enhance the use of TSX (JSX for TypeScript) within Vue 2 applications. Currently at version 3.2.0, this package primarily functions as a type checker, providing robust type safety for JSX syntax used in Vue 2 components. It is crucial to note that vue-tsx-support does not handle JSX transpilation; users must integrate a separate Babel preset (such as `@vue/babel-preset-jsx`) for this purpose. A key differentiator is its explicit focus on the Vue 2 ecosystem; it does not support Vue 3, which incorporates its own JSX type checking mechanisms that are incompatible. The library supports various component styles, including object-style, class-style (with `vue-class-component`), and `@vue/composition-api`, with specific instructions for each. The project is largely in a maintenance phase, as its core functionality is tied to the now older Vue 2 major version.
Common errors
-
TS2304: Cannot find name 'VueTsxSupport'.
cause The TypeScript compiler cannot find the global type declarations for `VueTsxSupport`.fixEnsure `jsxFactory` is set to `"VueTsxSupport"` in `tsconfig.json` AND that `import 'vue-tsx-support/enable-check';` is present in your main entry file, or `node_modules/vue-tsx-support/enable-check.d.ts` is included in your `tsconfig.json`. -
TS2604: JSX element 'div' has no corresponding closing tag.
cause Incorrect `jsx` compiler option in `tsconfig.json`, or a syntax error in your TSX code.fixVerify that `"jsx": "preserve"` is set in your `tsconfig.json`. Also, check your TSX file for unmatched tags or fragments which require `<>...</>` or `<React.Fragment>...</React.Fragment>` (though Vue JSX typically doesn't use fragments directly like React). -
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'IntrinsicElements'.
cause The global types for JSX intrinsic elements (like `div`, `span`, `input`) are not correctly loaded or configured.fixConfirm that `import 'vue-tsx-support/enable-check';` is present in your project's entry point or included in `tsconfig.json`. Also, check your `tsconfig.json`'s `jsxFactory` setting. -
TS2339: Property 'onClick' does not exist on type 'VNodeProperties'.
cause This error typically occurs when using custom components in TSX without properly defining their prop types for `vue-tsx-support`.fixEnsure your custom component is wrapped with `createTsxComponent<Props, Events>` and that the event names (e.g., `onClick` for an `@click` emit) are correctly specified in the `Events` interface.
Warnings
- breaking Projects migrating from `vue-tsx-support` v2 to v3 will encounter breaking changes. Refer to the 'Migration from V2' section in the package's README for detailed instructions on updating your codebase.
- breaking `vue-tsx-support` is explicitly designed for Vue 2 (>=2.6.0, <3.0.0) and does not support Vue 3. Vue 3 has its own native JSX type checking and incompatibilities exist.
- gotcha `vue-tsx-support` is solely a type checker and does not transpile JSX/TSX code into valid JavaScript. You *must* configure a separate Babel preset (e.g., `@vue/babel-preset-jsx` or `@vue/babel-preset-app`) in your build pipeline.
- gotcha Proper `tsconfig.json` configuration is critical. You must set `compilerOptions.jsx` to `"preserve"` and `compilerOptions.jsxFactory` to `"VueTsxSupport"` for TypeScript to correctly process TSX syntax.
- gotcha To enable full type checking for intrinsic HTML elements (like `<div>`, `<span>`) and custom components, you must import `vue-tsx-support/enable-check` once in your project, typically in `main.ts` or via `tsconfig.json`'s `include` array.
- gotcha When using `@vue/composition-api` with `vue-tsx-support`, specific Babel preset versions are required for correct transpilation. `@vue/babel-preset-jsx` >= 1.2.1 or `babel-preset-vue-vca` is necessary.
Install
-
npm install vue-tsx-support -
yarn add vue-tsx-support -
pnpm add vue-tsx-support
Imports
- enable-check.d.ts
import { enableCheck } from 'vue-tsx-support';import 'vue-tsx-support/enable-check';
- createTsxComponent
import { createTsxComponent } from 'vue-tsx-support'; - Component (class)
import { Component } from 'vue-tsx-support';
Quickstart
// tsconfig.json excerpt
// {
// "compilerOptions": {
// "jsx": "preserve",
// "jsxFactory": "VueTsxSupport",
// "target": "esnext",
// "module": "esnext",
// "strict": true
// },
// "include": [
// "src/**/*.ts",
// "src/**/*.tsx",
// "src/**/*.vue",
// "node_modules/vue-tsx-support/enable-check.d.ts" // Alternative to explicit import
// ]
// }
// src/components/MyCounter.tsx
import Vue from 'vue';
import { createTsxComponent } from 'vue-tsx-support';
interface MyCounterProps {
initialValue?: number;
label: string;
}
interface MyCounterEvents {
onChange: (newValue: number) => void;
}
const MyCounter = createTsxComponent<MyCounterProps, MyCounterEvents>({
name: 'MyCounter',
props: {
initialValue: { type: Number, default: 0 },
label: { type: String, required: true },
},
data() {
return { count: this.initialValue };
},
methods: {
increment() {
this.count++;
this.$emit('change', this.count);
},
decrement() {
this.count--;
this.$emit('change', this.count);
},
},
render() {
return (
<div>
<span>{this.label}: {this.count}</span>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
</div>
);
},
});
export default MyCounter;
// src/App.tsx (Example usage in a parent component)
import Vue from 'vue';
import MyCounter from './components/MyCounter';
// Crucial for global type checking of JSX intrinsic elements like 'div', 'span'
import 'vue-tsx-support/enable-check';
export default Vue.extend({
name: 'App',
data() {
return { totalCount: 0 };
},
methods: {
handleCounterChange(newValue: number) {
this.totalCount = newValue; // In a real app, this might accumulate counts
console.log(`Counter changed to: ${newValue}`);
},
},
render() {
return (
<div>
<h1>Vue TSX Application</h1>
<p>Current Total: {this.totalCount}</p>
<MyCounter label="Counter 1" initialValue={5} onChange={this.handleCounterChange} />
<MyCounter label="Counter 2" onChange={this.handleCounterChange} />
</div>
);
},
});