Code Monkey home page Code Monkey logo

vue-ts-types's Introduction

vue-ts-types

Lightweight TypeScript-first Vue prop type definitions

npm GitHub GitHub tests workflow

Example

import { defineComponent } from 'vue';
import {
  arrayProp,
  booleanProp,
  functionProp,
  isPositive,
  numberProp,
  oneOfProp,
  stringProp,
} from 'vue-ts-types';

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
  },
});

Motivation

Prop declarations are verbose

Declaring props is quite verbose, especially if you are using TypeScript and want to annotate more complex types (with PropType).

options: {
  type: Object as PropType<Options>,
  required: true,
}

// with vue-ts-types:
options: objectProp<Options>().required
Annotating optional complex props is error-prone

It's easy to forget using a union type with undefined or null when the prop is not required.

options: {
  type: Object as PropType<Options>, // wrong, it should be `Options | undefined`
  required: false,
}

// with vue-ts-types:
options: objectProp<Options>().optional // automatically typed as `Options | undefined`
Specifying both default and required can be contradictory

By specifying a prop's default value, the prop is automatically optional, even when required is set to true. See also the vue/no-required-prop-with-default ESLint rule.

disabled: {
  type: Boolean,
  required: true,
  default: false, // contradictory to `required: true`
}

// with vue-ts-types:
disabled: booleanProp().required // either required without default
disabled: booleanProp().withDefault(false) // or optional with default
Custom validation errors are not helpful

Since prop validators return only a boolean validation result, the reason why a value failed to validate is not printed in the console error.

age: {
  type: Number,
  required: true,
  validator: (age: unknown) => {
    return typeof age === 'number' && Number.isInteger(age) && age >= 18
  },
}

// with vue-ts-types:
age: integerProp((age: unknown) => {
  if (typeof age !== 'number' || age < 18) {
    return 'age should be a number of at least 18'
  }
  return undefined
}).required

Installation

npm install vue-ts-types

vue-ts-types has no dependencies and is tested to be compatible with Vue.js v2.6, v2.7 and v3.2.

Usage

Each of the prop functions returns an object with the following properties:

  • .optional: Use this to mark the prop as not required with a default value of undefined. Also includes undefined in the resulting prop type.
  • .nullable: Use this to mark the prop as not required with a default value of null. Also includes null in the resulting prop type.
  • .required: Use this to mark the prop as required without a default value.
  • .withDefault(value): Use this to set a default value for the prop. Note that the value has to fit the prop type. For non-primitive types, the value has to be a function that returns the default value.

ℹ️ Note:
Due to the way Vue props work, a prop's default value will only be used when passing undefined, not for null.
See issue #3135 in vuejs/vue.

Custom validator functions

Custom validator functions can be passed to any of the prop types. They are called with the value of the prop (type unknown) and should return a validation error message, or undefined if the value is valid. Validator functions do not influence type inference.

import { numberProp } from 'vue-ts-types';

type Validator = (value: unknown) => string | undefined;

const isPositive: Validator = (value) => {
  if (typeof value !== 'number' || value <= 0 || Number.isNaN(value)) {
    return 'value should be a positive number';
  }
  return undefined;
};

numberProp(isPositive).optional;
// → prop type: number | undefined

For convenience, some common validator functions are included in the library and can be imported just like prop types:

  • isNegative: only allows negative numbers (< 0)
  • isPositive: only allows positive numbers (> 0)
  • isNonNegative: only allows non-negative numbers (>= 0)
  • isNonPositive: only allows non-positive numbers (<= 0)

stringProp<T>(validator?: Validator)

Allows any string. No further runtime validation is performed by default.
Type parameter T can be used to restrict the type at compile time with a union type. (Consider using oneOfProp in this case.)

stringProp().optional;
// → prop type: string | undefined
stringProp().nullable;
// → prop type: string | null
stringProp().required;
// → prop type: string
stringProp().withDefault('foo');
// → prop type: string

type Foo = 'a' | 'b' | 'c';

stringProp<Foo>().optional;
// → prop type: Foo | undefined
stringProp<Foo>().nullable;
// → prop type: Foo | null
stringProp<Foo>().required;
// → prop type: Foo
stringProp<Foo>().withDefault('a');
// → prop type: Foo

booleanProp(validator?: Validator)

Allows any boolean (validated at runtime and compile time).

booleanProp().optional;
// → prop type: boolean | undefined
booleanProp().nullable;
// → prop type: boolean | null
booleanProp().required;
// → prop type: boolean
booleanProp().withDefault(false);
// → prop type: boolean

numberProp<T>(validator?: Validator)

Allows any number (validated at runtime and compile time). Type parameter T can be used to restrict the type at compile time with a union type. (Consider using oneOfProp in this case.)

numberProp().optional;
// → prop type: number | undefined
numberProp().nullable;
// → prop type: number | null
numberProp().required;
// → prop type: number
numberProp().withDefault(3.1415);
// → prop type: number

type Foo = 1 | 2 | 3;

numberProp<Foo>().optional;
// → prop type: Foo | undefined
numberProp<Foo>().nullable;
// → prop type: Foo | null
numberProp<Foo>().required;
// → prop type: Foo
numberProp<Foo>().withDefault(1);
// → prop type: Foo

integerProp(validator?: Validator)

Allows any integer (validated at runtime).

integerProp().optional;
// → prop type: number | undefined
integerProp().nullable;
// → prop type: number | null
integerProp().required;
// → prop type: number
integerProp().withDefault(42);
// → prop type: number

symbolProp(validator?: Validator)

Allows any symbol (validated at runtime and compile time).

symbolProp().optional;
// → prop type: symbol | undefined
symbolProp().nullable;
// → prop type: symbol | null
symbolProp().required;
// → prop type: symbol
symbolProp().withDefault(Symbol('foo'));
// → prop type: symbol

vueComponentProp(validator?: Validator)

Allows any Vue component instance, name or options object. No built-in runtime validation is performed by default.

vueComponentProp().optional;
// → prop type: VueComponent | undefined
vueComponentProp().nullable;
// → prop type: VueComponent | null
vueComponentProp().required;
// → prop type: VueComponent
vueComponentProp().withDefault('close-icon');
// → prop type: VueComponent

ℹ️ Note:
The type VueComponent is defined to be object | string. It has to be so broad to allow Vue 2 and Vue 3 component options or instances. If you are able to narrow the type without pulling in heavy dependencies, please open an issue or pull request!

anyProp<T>(validator?: Validator)

Allows any type. No built-in runtime validation is performed by default.
Type parameter T can be used to restrict the type at compile time.

anyProp().optional;
// → prop type: any
anyProp().nullable;
// → prop type: any
anyProp().required;
// → prop type: any
anyProp().withDefault('foo');
// → prop type: any

anyProp<string>().optional;
// → prop type: string | undefined
anyProp<string>().nullable;
// → prop type: string | null
anyProp<string>().required;
// → prop type: string
anyProp<string>().withDefault('foo');
// → prop type: string

arrayProp<T>(validator?: Validator)

Allows any array. No further runtime validation is performed by default.
Type parameter T can be used to restrict the type of the array items at compile time.

arrayProp().optional;
// → prop type: unknown[] | undefined
arrayProp().nullable;
// → prop type: unknown[] | null
arrayProp().required;
// → prop type: unknown[]
arrayProp().withDefault(() => []);
// → prop type: unknown[]

arrayProp<string>().optional;
// → prop type: string[] | undefined
arrayProp<string>().nullable;
// → prop type: string[] | null
arrayProp<string>().required;
// → prop type: string[]
arrayProp<string>().withDefault(() => ['foo', 'bar']);
// → prop type: string[]

objectProp<T>(validator?: Validator)

Allows any object. No further runtime validation is performed by default.
Type parameter T can be used to restrict the type at compile time.

objectProp().optional;
// → prop type: object | undefined
objectProp().nullable;
// → prop type: object | null
objectProp().required;
// → prop type: object
objectProp().withDefault(() => ({}));
// → prop type: object

interface User {
  name: string;
}

objectProp<User>().optional;
// → prop type: User | undefined
objectProp<User>().nullable;
// → prop type: User | null
objectProp<User>().required;
// → prop type: User
objectProp<User>().withDefault(() => ({ name: 'John' }));
// → prop type: User

functionProp<T>(validator?: Validator)

Allows any function. No further runtime validation is performed by default.
Type parameter T can be used to restrict the type to a specific function signature at compile time.

ℹ️ Note:
There is no .withDefault() function for this prop type.

functionProp().optional;
// → prop type: Function | undefined
functionProp().nullable;
// → prop type: Function | null
functionProp().required;
// → prop type: Function

type MyFunc = (a: number, b: string) => boolean;

functionProp<MyFunc>().optional;
// → prop type: MyFunc | undefined
functionProp<MyFunc>().nullable;
// → prop type: MyFunc | null
functionProp<MyFunc>().required;
// → prop type: MyFunc

oneOfProp<T>(allowedValues: readonly any[], validator?: Validator)

Allows any of the specified allowed values (validated at runtime and compile time).
Type parameter T can be used to adjust the inferred type at compile time, this is usually not necessary.

ℹ️ Note:
Proper type checking is only possible if the allowed values are readonly, usually through as const.

oneOfProp(['foo', 'bar'] as const).optional;
// → prop type: 'foo' | 'bar' | undefined
oneOfProp(['foo', 'bar'] as const).nullable;
// → prop type: 'foo' | 'bar' | null
oneOfProp(['foo', 'bar'] as const).required;
// → prop type: 'foo' | 'bar'
oneOfProp(['foo', 'bar'] as const).withDefault('foo');
// → prop type: 'foo' | 'bar'

oneOfObjectKeysProp<T>(object: object, validator?: Validator)

Allows any of the keys of the specified object (validated at runtime and compile time).
Type parameter T can be used to adjust the inferred type at compile time, this is usually not necessary.

oneOfObjectKeysProp({ foo: 1, bar: 2 }).optional;
// → prop type: 'foo' | 'bar' | undefined
oneOfObjectKeysProp({ foo: 1, bar: 2 }).nullable;
// → prop type: 'foo' | 'bar' | null
oneOfObjectKeysProp({ foo: 1, bar: 2 }).required;
// → prop type: 'foo' | 'bar'
oneOfObjectKeysProp({ foo: 1, bar: 2 }).withDefault('foo');
// → prop type: 'foo' | 'bar'

oneOfTypesProp<T>(type: PropType<T>, validator?: Validator)

Allows any of the passed constructor types (validated at runtime).
Type parameter T has to be used to adjust the type at compile time.

oneOfTypesProp<number | string>([Number, String]).optional;
// → prop type: string | number | undefined
oneOfTypesProp<number | string>([Number, String]).nullable;
// → prop type: string | number | null
oneOfTypesProp<number | string>([Number, String]).required;
// → prop type: string | number
oneOfTypesProp<number | string>([Number, String]).withDefault(42);
// → prop type: string | number

instanceOfProp<T>(parent: T, validator?: Validator)

Allows instances of the given constructor (validated at runtime and compile time).
Type parameter T can be used to adjust the inferred type at compile time.

instanceOfProp(Date).optional;
// → prop type: Date | undefined
instanceOfProp(Date).nullable;
// → prop type: Date | null
instanceOfProp(Date).required;
// → prop type: Date
instanceOfProp(Date).withDefault(() => new Date());
// → prop type: Date

Contributing

Please see CONTRIBUTING.md.

Similar packages

License

Unless otherwise noted, all source code is licensed under the MIT License.
Copyright (c) 2022 Flo Edelmann

vue-ts-types's People

Contributors

dependabot[bot] avatar floedelmann avatar jhoermann avatar mrazauskas avatar ricayanzon avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

vue-ts-types's Issues

New validators

Adding new validators helps avoiding boilerplate code during Vue app development. If you are missing a common validator, feel free to suggest it here or to implement it yourself. See CONTRIBUTING.md for how to get started.

Improve documentation

If you are missing anything from the documentation (both readme and JSDoc comments in the code), feel free to suggest it here or submit a pull request!

Make `numberProp` generic

Sometimes only specific numbers are allowed. numberProp currently does not allow to restrict the type at compile time with a union type, like stringProp does. This could be useful for enum values.

Basically, re-implement #19 for numberProp.

Migrate type tests to TSTyche

Hey there!

Just wanted to draw your attention that tsd-lite got deprecated. It is used in your project by the jest-runner-tsd.

The architecture of tsd was very limited and did not scale well. jest-runner-tsd was initially created to test type in Jest repo. I was contributing type tests to the repo for some time. Currently there are around 1200 assertions. At this scale it became painful to debug or add new type features.

You have loads of tests, so the problems should sound familiar. This is why I went on building TSTyche. I have spending two years researching and experimenting. Now it is ready for use. It is made to scale, to be fast and to last for years.

How exactly? For instance, it has testing helpers like describe() or it() with .only, .skip, .todo run mode modifiers. These are priceless if you want to focus on one feature.

It ships with expect style assertions that can be passed an expression or a type on both side. This means that types can be compared directly without creating intermediate values. The run mode modifiers can be used directly on assertions like expect.fail, expect.only or expect.skip.

TSTyche has a lot to offer. It is fast and lightweight (zero dependencies, install size below 200kB).

Currently TSTyche is used by Immutable.js, Jest, Redwood and few more smaller project (including my own packages).

Repo: https://github.com/tstyche/tstyche
Documentation: https://tstyche.org


Would you be interested to migrate to TSTyche? This shouldn’t be hard, because you already structured files using describe().

I am happy to help setting up TSTyche and updating test files. Just wanted to ask before opening a PR.

New prop types

Adding new prop types helps avoiding boilerplate code during Vue app development. If you are missing a prop type, feel free to suggest it here or to implement it yourself. See CONTRIBUTING.md for how to get started.

Create ESLint plugin

Package name: eslint-plugin-vue-ts-types

Possible rules:

vue-ts-types/no-any-prop

Reports use of anyProp.

vue-ts-types/prefer-array-prop

Reports objectProp<User[]> and suggests arrayProp<User> instead.

vue-ts-types/prefer-one-of-prop

Reports stringProp<'a' | 'b'> and numberProp<1 | 2> and suggests oneOfProp(['a', 'b'] as const) etc. instead.

vue-ts-types/require-one-of-prop-as-const

Reports use of oneOfProp([…]) without as const.

vue-ts-types/require-type-parameter

Reports use of prop functions without type parameter (by default only for arrayProp, objectProp, functionProp and oneOfTypesProp, but others can be configured).

vue-ts-types/require-vue-ts-types

Reports where vue-ts-types is not used (but should); autofix would be very helpful.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.