Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

API input validators with user-friendly error output and TypeScript to ensure you don't miss any properties

License

NotificationsYou must be signed in to change notification settings

jcoreio/typescript-validators

Repository files navigation

CircleCICoverage Statussemantic-releaseCommitizen friendlynpm version

Complex type validators that generate TypeScript types for you.The validation errors are detailed. Adapted from the brilliant work inflow-runtime.

Deprecated

I recreated this project astyped-validators and added Flow support!

A few breaking changes to the API were necessary for Flow support, but they also made it easier to declare objects withoptional properties.

Table of Contents

Introduction

When you need to validate the inputs to a TypeScript API, a problem arises. How do you ensure that a value that passes validationmatches your declared TypeScript type? Someone might modify one and forget to modify the other:

typePost={author:{name:stringusername:string}content:string// newly added by developertags:string[]}// hypothetical syntaxconstvalidator=requireObject({author:requireObject({name:requireString(),username:requireString(),}),content:requireString(),// uhoh!! developer forgot to add tags here})

typescript-validators solves this by generating TypeScript types from your validators:

import*astfrom'typescript-validators'constPostValidator=t.simpleObject({author:t.simpleObject({name:t.string(),username:t.string(),}),content:t.string(),tags:t.array(t.string()),})typePost=t.ExtractType<typeofPostValidator>constexample:Post=PostValidator.assert({author:{name:'MC Hammer',username:'hammertime',},content:"Can't touch this",tags:['mc-hammer','hammertime'],})

Hover overPost in VSCode and you'll see, voilà:

typePost={author:{name:stringusername:string}content:stringtags:string[]}

What about generating validators from type defs?

I'd like to be able to do this, because type defs are a lot more readable. In fact, for Flow, it's possible withbabel-pluging-flow-runtime, which I have a lot of experience with. That looks like this:

import{typeType,reify}from'flow-runtime'typePost={author:{name:stringusername:string}content:stringtags:string[]}constPostValidator=(reify:Type<Post>)// looooots of magic hereconstexample:Post=PostValidator.assert({author:{name:'MC Hammer',username:'hammertime',},content:"Can't touch this",tags:['mc-hammer','hammertime'],})

This is sweet but there are some caveats:

  • You have to add a Babel plugin to your toolchain (for TypeScript, not everyone wants to use Babel)
  • There are issues with the Babel plugin. It aims to support all Flow types, with varying success.
  • The original author offlow-runtime abandoned the project and I don't blame him. It was hugely ambitious and difficult to maintain.

The author offlow-runtime himself told me in private conversations that he had moved on to an approach liketypescript-validators in his own projects, because generating types from the validator declarations is a lotsimpler and more maintainable in the long run.

API

I recommend importing like this:

import*astfrom'typescript-validators'

Type creators

All of the following methods return an instance oft.Type<T>.

t.boolean()

A validator that requires the value to be aboolean.

t.boolean(true)

A validator that requires the value to betrue.

t.string()

A validator that requires the value to be astring.

t.string('foo')

A validator that requires the value to be'foo'.

t.number()

A validator that requires the value to be anumber.

t.number(3)

A validator that requires the value to be3.

t.symbol()

A validator that requires the value to be asymbol.

t.symbol(MySymbol)

A validator that requires the value to beMySymbol.

t.null() /t.nullLiteral()

A validator that requires the value to benull.

t.nullOr(t.string())

A validator that requires the value to bestring | null

t.undefined() /t.undefinedLiteral()

A validator that requires the value to beundefined.

t.nullish()

A validator that requires the value to benull | undefined.

t.nullishOr(t.string())

A validator that requires the value to bestring | null | undefined.

t.array(t.number())

A validator that requires the value to benumber[].

t.simpleObject({ foo: t.string() })

A validator that requires the value to be an object with only afoo property that's astring.

t.object

For dealing with optional properties, use the following.The syntax is a bit awkward but it's the best way I could find to get a clean type output:

constThingValidator=t.object<{name:anycomment?:any}>()({name:t.string(),comment:t.optional(t.string()),})typeThing=t.ExtractType<typeofThingValidator>

The type ofThing will be{ name: string, comment?: string }. Note that the property types in the explicit type parameter(any) are ignored. The type parameter just indicates which properties are required and which are optional, and also allowsyou to mark properties readonly. These attributes will be reflected int.ExtractType.

You can also use thet.optionalNullOr(t.string()) as a shorthand fort.optional(t.nullOr(t.string())).

t.record(t.string(), t.number())

A validator that requires the value to beRecord<string, number>.

t.instanceOf(Date)

A validator that requires the value to be an instance ofDate.

t.tuple(t.string(), t.number())

A validator that requires the value to be[string, number].Accepts a variable number of arguments.

t.allOf(A, B)

A validator that requires the value to beA & B. Accepts a variable number of arguments, though type generation is only overloaded up to 8 arguments. For example:

constThingType=t.simpleObject({name:t.string()})constCommentedType=t.simpleObject({comment:t.string()})constCommentedThingType=t.allOf(ThingType,CommentedType)CommentedThingType.assert({name:'foo',comment:'sweet'})

t.oneOf(t.string(), t.number())

A validator that requires the value to bestring | number. Accepts a variable number of arguments, though type generation is only overloaded up to 8 arguments.

t.alias(name, type)

Creates aTypeAlias with the givenname andtype.

Type aliases serve two purposes:

t.ref(() => typeAlias)

Creates a reference to the givenTypeAlias. SeeRecursive Types for examples.

t.Type<T>

The base class for all validator types.

T is the type of values it accepts.

accepts(input: any): boolean

Returnstrue if and only ifinput is the correct type.

assert<V extends T>(input: any, prefix = '', path?: (string | number | symbol)[]): V

Throws an error ifinput isn't the correct type.

prefix will be prepended to thrown error messages.

path will be prepended to validation error paths. If you are validating a function parameter namedfoo,pass['foo'] forpath to get clear error messages.

validate(input: any, prefix = '', path?: (string | number | symbol)[]): Validation<T>

Validatesinput, returning any errors in theValidation.

prefix andpath are the same as inassert.

warn(input: any, prefix = '', path?: (string | number | symbol)[]): void

Logs a warning to the console ifinput isn't the correct type.

toString(): string

Returns a string representation of this type (using TS type syntax in most cases).

t.ExtractType<T extends Type<any>>

Gets the TypeScript type that a validator type accepts. For example:

import*astfrom'typescript-validators'constPostValidator=t.simpleObject({author:t.simpleObject({name:t.string(),username:t.string(),}),content:t.string(),tags:t.array(t.string()),})typePost=t.ExtractType<typeofPostValidator>

Hover overPost in the IDE and you'll see, voilà:

typePost={author:{name:stringusername:string}content:stringtags:string[]}

t.TypeAlias<T>

readonly name: string

The name of the alias.

addConstraint(...constraints: TypeConstraint<T>[]): this

Adds custom constraints.TypeConstraint<T> is a function(value: T) => string | null | undefined whichreturns nullish ifvalue is valid, or otherwise astring describing whyvalue is invalid.

Custom Constraints

It's nice to be able to validate that something is anumber, but what if we want to make sure it's positive?We can do this by creating a type alias fornumber and adding a custom constraint to it:

constPositiveNumberType=t.alias('PositiveNumber',t.number()).addConstraint((value:number)=>(value>0 ?undefined :'must be > 0'))PositiveNumberType.assert(-1)

The assertion will throw at.RuntimeTypeError with the following message:

Value must be > 0Expected: PositiveNumberActual Value: -1Actual Type: number

Recursive Types

Creating validators for recursive types takes a bit of extra effort. Naively, we would want to do this:

constNodeType=t.object<{value:anyleft?:anyright?:any}>()({value:t.any(),left:t.optional(NodeType),right:t.optional(NodeType),})

Butt.optional(NodeType) causes the errorBlock-scoped variable 'NodeType' referenced before its declaration.

To work around this, we can create aTypeAlias and a reference to it:

constNodeType:t.TypeAlias<{value:anyleft?:Noderight?:Node}>=t.alias('Node',t.object<{value:anyleft?:anyright?:any}>()({value:t.any(),left:t.optional(t.ref(()=>NodeType)),right:t.optional(t.ref(()=>NodeType)),}))typeNode=t.ExtractType<typeofNodeType>NodeType.assert({value:'foo',left:{value:2,right:{value:3,},},right:{value:6,},})

Notice how we use a thunk function int.ref(() => NodeType) to avoid referencingNodeType before its declaration.

About

API input validators with user-friendly error output and TypeScript to ensure you don't miss any properties

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

[8]ページ先頭

©2009-2025 Movatter.jp