Vue TypeScript Prop Type Definitions
vue-ts-types is a lightweight, TypeScript-first library designed to simplify and enhance the definition of Vue.js component props. It provides a fluent API for declaring prop types, addressing common pain points such as verbose prop declarations (especially when using Vue's built-in PropType utility), error-prone optional complex prop annotations (e.g., forgetting to union with `| undefined`), contradictions between `required` and `default` properties which lead to ambiguous behavior and ESLint warnings, and the inability to provide helpful custom validation error messages beyond a boolean result. The library is currently at version 1.9.0 and maintains an active release cadence with frequent minor updates. It supports both Vue 2.6+ and Vue 3.2+, shipping with full TypeScript type definitions. Since v1.9.0, it provides dual CommonJS and ES Module support, ensuring broad compatibility. Its core value lies in making prop definitions significantly more concise, type-safe, and less prone to common development errors in Vue projects utilizing TypeScript.
Common errors
-
Module parse failed: Unexpected token 'export' (at line X, column Y) You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/configuration/module/#rule-type
cause This error typically occurs when a CommonJS-based environment or build setup attempts to import `vue-ts-types` as an ES Module, or if your tooling is not correctly configured to resolve dual packages.fixFor projects using modern tooling, ensure your `tsconfig.json`'s `moduleResolution` (e.g., set to 'Bundler' or 'NodeNext') and your bundler's configuration (e.g., Webpack, Rollup, Vite) are set up to correctly resolve ES Modules. If explicitly targeting CommonJS, ensure your build picks up the CJS bundle; since v1.9.0, both are provided via the `exports` map in `package.json`. -
[vue/no-required-prop-with-default] The 'required' prop 'X' should not have a default value.
cause This ESLint error, part of `eslint-plugin-vue`, indicates that a Vue prop has been declared as both `required: true` and includes a `default` value. This is a logical contradiction, as a default value inherently makes a prop optional.fixWhen using `vue-ts-types`, you should explicitly choose either the `.required` chainable method (for props that must always be provided) or the `.withDefault(value)` method (for optional props with a fallback). The library's API design prevents you from making this contradictory declaration. -
Type 'X' is not assignable to type 'PropType<Y>'. Property 'Z' is missing in type 'X' but required in type 'Y'.
cause This TypeScript error often points to a mismatch between the expected prop type and the provided type. It can occur if the generic type argument passed to `vue-ts-types` helpers is incorrect, or if an optional prop is not correctly typed to include `undefined`.fixVerify that the generic type argument provided to functions like `arrayProp<T>()`, `objectProp<T>()`, or `functionProp<T>()` precisely matches the intended data structure. For props that are optional at runtime, ensure you use the `.optional` or `.nullable` modifiers (e.g., `stringProp().optional`) to correctly infer the `T | undefined` or `T | null` type, thus satisfying TypeScript's strict type checking.
Warnings
- breaking Version 1.7.0 mistakenly switched the package to ES Modules (ESM) only, causing immediate breaking changes for existing CommonJS (CJS) users. This was swiftly reverted in v1.7.1, but highlights potential module resolution issues. Since v1.9.0, dual ESM and CJS builds are officially provided, but careful configuration of your build tooling (e.g., Webpack, Rollup, Vite) is crucial to ensure it picks the correct module format for your target environment.
- gotcha Attempting to combine `required: true` with a `default` value in standard Vue prop definitions is contradictory. Vue implicitly treats any prop with a `default` as optional, regardless of the `required` flag, leading to unexpected behavior and often triggering the `vue/no-required-prop-with-default` ESLint rule. `vue-ts-types` is designed to prevent this logical conflict.
- gotcha A common TypeScript footgun in Vue is failing to explicitly union `PropType` with `undefined` or `null` for optional or nullable complex props (e.g., `PropType<MyType>` instead of `PropType<MyType | undefined>`). This results in incorrect type inference, leading to TypeScript errors when the prop might legitimately be absent or null.
Install
-
npm install vue-ts-types -
yarn add vue-ts-types -
pnpm add vue-ts-types
Imports
- stringProp
const stringProp = require('vue-ts-types').stringPropimport { stringProp } from 'vue-ts-types' - oneOfProp
import { oneOfProp as customOneOfProp } from 'vue-ts-types/dist/oneOfProp'import { oneOfProp } from 'vue-ts-types' - isPositive
const isPositive = require('vue-ts-types').isPositiveimport { isPositive } from 'vue-ts-types'
Quickstart
import { defineComponent, createApp } from 'vue';
import {
arrayProp,
booleanProp,
functionProp,
isPositive,
numberProp,
oneOfProp,
stringProp
} from 'vue-ts-types';
const MyComponent = defineComponent({
props: {
disabled: booleanProp().withDefault(false),
// resulting prop type: boolean
title: stringProp().optional,
// resulting prop type: string | undefined
description: stringProp().nullable,
// resulting prop type: string | null
items: arrayProp<string>().required,
// resulting prop type: string[]
callback: functionProp<() => void>().optional,
// resulting prop type: (() => void) | undefined
color: oneOfProp(['red', 'green', 'blue'] as const).withDefault('red'),
// resulting prop type: 'red' | 'green' | 'blue'
timeout: numberProp(isPositive).required,
// resulting prop type: number
},
template: `
<div>
<h1 v-if="title">{{ title }}</h1>
<p v-if="description">{{ description }}</p>
<button :disabled="disabled" @click="callback">Click Me</button>
<ul>
<li v-for="item in items" :key="item">{{ item }}</li>
</ul>
<p>Selected color: {{ color }}</p>
<p>Timeout (ms): {{ timeout }}</p>
</div>
`,
setup(props) {
// console.log(props.timeout);
return { };
}
});
// To run this in a browser, you would typically mount it:
// const app = createApp(MyComponent);
// app.mount('#app');
// Example usage if you were creating a root app:
const app = createApp({
components: { MyComponent },
template: `<MyComponent title="Hello Vue Props" :items="['Apple', 'Banana']" :timeout="100" />`
});
// In a real application, you'd mount this to an element in your HTML
// app.mount('#app'); // Assuming you have <div id="app"></div> in your HTML