Extending VueTypes
Standalone custom validators
The toType
, toValidableType
and fromType
functions can be used to create custom standalone validators. Indeed, they are used internally by vue-types
in native and custom validators.
Custom validators from scratch
In the following example we define a validator for positive numbers:
const positive = () =>
toType('positive', {
type: Number,
validator: (v) => v >= 0,
})
export default {
props: {
myNumber: positive().isRequired,
},
}
Both toType
and toValidableType
accept the following arguments:
name | type | description |
---|---|---|
name | string | The validator name, used for logging |
type | object | An object compatible with Vue.js prop validation |
TIP
The difference between toType
and toValidableType
is that the latter creates validators that support the .validate()
method to setup custom validation functions.
Inheriting from existing validators
In some cases you might want to use an already defined validator or validator instance as base for a new validator. This might come handy for code reusability.
In this case you can use the fromType
utility function.
Function arguments:
name | type | required | description |
---|---|---|---|
name | string | yes | The validator name, used for logging |
source | validator | yes | Parent prop validator |
props | object | - | custom validation properties |
import { fromType, shape, number, string } from 'vue-types'
const user = shape({
ID: number(),
name: string(),
})
// clone the user shape and make it required
const userRequired = fromType('userRequired', user, { required: true })
WARNING
Properties defined in the 3rd argument will overwrite those defined in the base validator.
The only exception is validator()
: those functions will be merged and executed in sequence until one returns false
.
import { fromType, shape, number, string } from 'vue-types'
const userRequired = shape({
ID: number(),
name: string(),
}).isRequired
// userJohn is not required
// after validating the shape
// will check that name === 'John'
const userJohn = fromType('userJohn', user, {
required: false,
validator(value) {
return value.name === 'John'
},
})
This function can be used to mimic the Inherit from VueTypes validators pattern in VueTypes.extend
:
import { fromType, shape, number, string } from 'vue-types'
const userShape = VueTypes.shape({ name: String, age: Number })
const userJohn = () =>
fromType('userJohn', userShape, {
validator(value) {
return value && value.name === 'John'
},
})
export default {
props: {
user: userDoe().isRequired,
},
}
Extending namespaced validators in ES6+
If your source code supports ES6 or newer, you can use the native ES extends
feature with the toType
, toValidableType
or fromType
utility functions (see Standalone custom validators for detailed usage instructions).
For example, you could create a prop-types.js
file in your project and export the extended VueTypes class:
// prop-types.js
import VueTypes, { toType, toValidableType } from 'vue-types'
export default class ProjectTypes extends VueTypes {
// define a custom validator that accepts configuration parameters
static maxLength(max) {
return toType('maxLength', {
type: String,
validator: (max, v) => v.length <= max,
})
}
// a native-like validator that supports the `.validable` method
static get positive() {
return toValidableType('positive', {
type: Number,
validator: (v) => v > 0,
})
}
}
Usage:
<!-- MyComponent.vue -->
<template>
<!-- template here -->
</template>
<script>
import ProjectTypes from './prop-types'
export default {
name: 'MyComponent',
props: {
msg: ProjectTypes.maxLength(2),
count: ProjectTypes.positive,
},
}
</script>
TYPESCRIPT HINT
This pattern ensures type safety using the language built-in features and might be the most efficient and readable option for TypeScript projects.
The extend()
method deprecated
WARNING
As of v5.0, the extend()
method is deprecated and might be removed in a future major version of the library.
The recommended way to extend a namespaced VueTypes is by using the ES6+ method.
You can extend the namespaced VueTypes object with your own validators via VueTypes.extend({...})
.
The method accepts an object with every key supported by Vue prop validation objects plus the following custom properties:
name | type | default | description |
---|---|---|---|
name | string | - | Required. The type name. Will be exposed as VueType.{name} |
validate | boolean | false | If true , the type will have a validate method like native types |
getter | boolean | false | Set the validator as a getter (1) |
- If
true
the validator will be defined as an accessor property (like, for example,VueTypes.string
). Iffalse
it will be defined as a method (likeVueTypes.arrayOf()
).
Examples:
// as an accessor
VueTypes.extend({
name: 'negative',
getter: true,
type: Number,
validator: (v) => v < 0,
})
const negativeProp = VueTypes.negative
// as a configurable method
VueTypes.extend({
name: 'negativeFn',
type: Number,
validator: (v) => v < 0,
})
const negativeProp2 = VueTypes.negativeFn() // <-- you need to call it
Note that if getter === false
, arguments passed to the validator function will be passed down to the validator
method together with the prop value:
VueTypes.extend({
name: 'maxLength',
// getter: false, this is the default
type: String,
validator: (max, v) => v.length <= max,
})
// set the "max" argument of the validator method
const maxLengthType = VueTypes.maxLength(2)
maxLengthType.validator('ab') // true
maxLengthType.validator('abcd') // false
Inherit from VueTypes validators
You can set another validator as the parent of a new one by setting it as the type
property. This feature can be useful to create named aliases:
const shoesShape = VueTypes.shape({ color: String, size: Number })
VueTypes.extend({
name: 'shoes',
getter: true,
type: shoesShape,
})
export default {
props: {
shoes: VueTypes.shoes,
// same as
// shoes: shoesShape
},
}
Custom validators will be executed before the parent's one:
// ...
VueTypes.extend({
name: 'redShoes',
getter: true,
type: shoesShape,
validator(value) {
return value && value.color === 'red'
},
})
const myShoes = { color: 'blue', size: 11 }
console.log(VueTypes.utils.validate(myShoes, VueTypes.redShoes)) // false
WARNING
Validators created with this method don't support the validate
method even if their parent supports it (like VueTypes.string
or VueTypes.number
).
VueTypes.extend({
name: 'myString',
getter: true,
type: VueTypes.string,
})
VueTypes.myString.validate(/* ... */)
// Error: validate() is not defined
Define multiple validators
To define multiple validators at once, pass an array of definitions as argument:
// ...
VueTypes.extend([
{
name: 'negative',
getter: true,
type: Number,
validator: (v) => v < 0,
},
{
name: 'positive',
getter: true,
type: Number,
validator: (v) => v > 0,
},
])
// usage...
VueTypes.positive.isRequired
VueTypes.negative
Typescript
When used in a TypeScript project, validators added via .extend()
might fail type checking.
To instruct TypeScript about your custom validators you can use the following pattern:
1. First create a module to host your extended VueTypes object:
// prop-types.ts
// 1. import
// - VueTypes library
// - VueTypes class interface
// - validation object interface (VueTypeDef or VueTypeValidableDef)
import VueTypes, {
VueTypesInterface,
VueTypeDef,
VueTypeValidableDef,
} from 'vue-types'
// 2. define your custom VueTypes validators interface
interface ProjectTypes extends VueTypesInterface {
// VueTypeDef accepts a type as argument
maxLength(max: number): VueTypeDef<string>
// use VueTypeValidableDef if the new type is going to support the `validate` method.
positive: VueTypeValidableDef<number>
}
// 3. Pass the interface as type argument to the `extend` method
export default VueTypes.extend<ProjectTypes>([
{
name: 'maxLength',
type: String,
validator: (max: number, v: string) => v.length <= max,
},
{
name: 'positive',
getter: true,
type: Number,
validator: (v: number) => v > 0,
},
])
2. Then import the newly created prop-types.ts
instead of vue-types
:
<!-- MyComponent.vue -->
<template>
<!-- template here -->
</template>
<script lang="ts">
import Vue from 'vue'
import VueTypes from './prop-types'
export default Vue.extend({
name: 'MyComponent',
props: {
msg: VueTypes.maxLength(2),
count: VueTypes.positive,
},
})
</script>