GithubHelp home page GithubHelp logo

vedantroy / typecheck.macro Goto Github PK

View Code? Open in Web Editor NEW
414.0 6.0 7.0 870 KB

A easy (and fast) typescript validation library/macro. Automatically generate validation functions for Typescript types.

License: MIT License

TypeScript 96.29% JavaScript 3.71%

typecheck.macro's Introduction

logo

Automatically generate 🔥 blazing🔥 fast validators for Typescript types.

Babel Macro

Example

type Cat<T> = {
    breed: "tabby" | "siamese";
    isNice: boolean
    trinket?: T;
}
registerType('Cat')
// Turned into a validation function at compile time through the power of babel macros
// You can also use createDetailedValidator to get error messages
const isNumberCat = createValidator<Cat<number>>()
isNumberCat({ breed: "tabby", isNice: false })                 // true
isNumberCat({ breed: "corgi", isNice: true, trinket: "toy" })  // false

Purpose

Because Typescript types are erased at compile time you can't use them to validate data at runtime. For example, you might want to ensure an API is returning data that matches a given type at runtime. This library (macro) generates validation functions for your Typescript types at compile time.

Why this macro?

Ease of Use

With typecheck.macro you can write normal Typescript types and automatically get validation functions for them. Other validation libraries require you to write your types in a DSL. Thus, typecheck.macro naturally integrates into your project without requiring you to change any existing code.

typecheck.macro supports a large portion of the Typescript type system (support table) so you can validate most of your existing types automatically.

typecheck.macro has features, such as comprehensive error messages and automatic detection and support of circular types, that other projects do not have.

Performance

typecheck.macro generates specialized validation functions that are pure Javascript at compile time. (Almost) every other library generates generic data structures that are plugged into a generic validator function.

typecheck.macro is up to 3x faster than ajv, the fastest JSON schema validator. And anywhere from 6 to 500 times faster than popular libraries, like runtypes or zod.

typecheck.macro is smart. It will analyze your type and determine the fastest/most minimal validation function that can validate your type. For example, the type "Hello" | string will automatically be simplified to string and the type A in type A = B | number; type B = string | C; type C = string will automatically be simplified to type A = string | number, and the appropriate validation code will be generated.

Installation

If you are using Gatsby or Create React App, you can just install the macro. No other steps needed!

Otherwise, you will need to switch over to compiling your Typescript with Babel. This isn't difficult since Babel has good Typescript support. See the example.

Step by Step Instructions

  1. Install dependencies for compiling Typescript with Babel and using macros. [pnpm|npm|yarn] install --save-dev @babel/core @babel/cli @babel/preset-typescript @babel/plugin-transform-modules-commonjs babel-plugin-macros typecheck.macro
    • @babel/plugin-transform-modules-commonjs is so export and import are turned into module.exports and require, so your code will work in Node.
  2. Add the file babel.config.json to the root of your project with the contents:
{
  "presets": ["@babel/preset-typescript"],
  "plugins": ["babel-plugin-macros", "@babel/plugin-transform-modules-commonjs"]
}
  1. Add the command babel src --out-dir dist --extensions \".ts\" to your "package.json". All typescript files in "src" will be compiled (with the macro enabled) to the dist directory.

Usage

Basic Usage

In addition to reading this, read the example.

import createValidator, { registerType } from 'typecheck.macro'

type A = {index: number, name: string}
registerType('A')
// named type
const validator = createValidator<A>()
// anonymous type
const validator2 = createValidator<{index: number, name: string}>()
// mix and match (anonymous type that contains a named type)
const validator3 = createValidator<{index: number, value: A}>()

registerType(typeName: string)

If you want to validate a named type or an anonymous type that references a named type, you must register the named type.

typeName is the name of the type you want to register. The type declaration must be in the same scope of the call to registerType.

{
    type A = {val: string}
    registerType('A') // registers A
    {
        registerType('A') // does nothing :(
    }
}
registerType('A') // does nothing :(

All registered types are stored in a per-file global namespace. This means any types you want to register in the same file should have different names.

registering a type in one file will not allow it to be accessible in another file. This means you cannot generate validators for multi-file types (a type that references a type imported from another file). If this is a big issue for you, go to the "Caveats" section.

A work-around for supporting multi-file types is to move your multi-file types into one file (so they are no longer multi-file types). Then generate the validation functions in that file and export to them where you want to use them. This works because validation functions are just normal Javascript!

registerType automatically registers all types in the same scope as the original type it is registering that are referred to by the original type.

type A = {val: string}
type B = {val: A}
type C = {val: A}
// registers A and B, but not C, since B only refers to A.
registerType('B')

All instances of the registerType macro are evaluated before any instance of createValidator. So ordering doesn't matter.

Most of the primitive types (string, number, etc.) are already registered for you. As are Array, Map, Set and their readonly equivalents.

createValidator<T>(opts?: BooleanOptions, userFuncs?: UserFunctions): (value: unknown) => value is T

Creates a validator function for the type T.

T can be any valid Typescript type/type expression that is supported by the macro.

At compile time, the call to createValidator will be replaced with the generated code.

BooleanOptions: {circularRefs?: boolean, allowForeignKeys?: boolean}

  • allowForeignKeys
    • Default: true.
    • If false, then any unexpected/extra keys in objects will throw a validation error. Note: If you are using a string index signature then there is no such thing as an extra key. And if you are using just a numeric index signature, then there is no such thing as an extra key with a numeric value. This is consistent with typescript.
  • circularRefs
    • Default: true
    • If false, then any circular references in the object will result in an infinite-loop at runtime. Note: circular types, such as type A = {next: A } | null will still work if this option is set to false. However, true circular references (instead of just another layer of nesting) in an input object will not work.

createDetailedValidator<T>(opts?: DetailedOptions, userFuncs?: UserFunctions)

Full type signature:

function createDetailedValidator<T>(
  opts?: DetailedOptions
): (
  value: unknown,
  errs: Array<[string, unknown, IR.IR | string]>
) => value is T;

Creates a detailed validator function for the type T. Example usage:

const v = createDetailedValidator<{x: string}>()
const errs = []
const result = v({x: 42}, errs) // result is false
// errors = [["input["x"]", 42, "type: string"]]

The resulting validation function takes 2 parameters:

  • value, the value you want to validate
  • errs, an array which will be populated with all the validation errors (if there are any). Each entry in errs is a tuple of 3 elements:
    • the path in the object at which validation failed (string)
    • the value at that path (any)
    • the expected value at that path

DetailedOptions: BooleanOptions & { expectedValueFormat: string }

  • expectedValueFormat
    • Default: "human-friendly"
    • If "human-friendly" then the expected value format will be a human friendly description of the types.
    • If "type-ir" then the expected value will be a JSON object representing the macro's internal representation of the expected type. It is not recommended to use this option because the internal representation is unstable and not bound by semver.

Constraints

What if you want to enforce arbitrary constraints at runtime? For example, ensure a number in an interface is always positive. You can do this with constraints. You can enforce an arbitary runtime constraint for any user-defined type (e.g not number, string, etc.).

The type of the 2nd parameter of both createValidator and createDetailedValidator:

type UserFunction = { constraints: { [typeName: string]: Function } }

Context

type NumberContainer = {
  pos: Positive;
}
type Positive = number;

With Boolean Validation

const x = createValidator<NumberContainer>(undefined, {
  constraints: {
    Positive: x => x > 0
  }
})

Notes:

  • The Positive key in the constraints object corresponds to the user-defined type Positive.
  • The value must be a function expression. It cannot be a variable that refers to a function because the macro is evaluated at compile time.
  • The constraint is only called after its base type has been validated. In this instance, the "Positive" constraint will only be called after input.pos is validated to be a number.

With Detailed Validation

const x = createDetailedValidator<NumberContainer>(undefined, {
  constraints: {
    Positive: x => x > 0 ? null : "expected a positive number"
  }
})

Note: The constraint function returns an error (if there is one). Otherwise it returns a falsy value. Any truthy value will be treated as an error message/object.

Support Tables

See the exec tests to get a good idea of what is supported

Primitives Types

Primitives Support
number Yes
string Yes
boolean Yes
object Yes
any Yes
unknown Yes
BigInt WIP
Symbol WIP

Builtin Generic Types

Type Support Notes
Array Yes
ReadonlyArray Yes Same as Array at runtime.
Map Yes
ReadonlyMap Yes Same as Map at runtime.
Set Yes
ReadonlySet Yes Same as Set at runtime.

Typescript Concepts

Language Features Support Notes
interface Yes extending another interface is WIP
type alias Yes
generics Yes
union types Yes
tuple types Yes
arrays Yes
index signatures Yes
literal types Yes
circular references Yes
parenthesis type expressions Yes
intersection types Yes One caveat (caveats)
Mapped Types WIP
Multi-file types iffy Requires CLI tool instead of macro
User-declared classes No

Performance Table

Notes:

  • The numbers are nanoseconds.
  • ajv is the current fastest JSON schema validator

Boolean Validator

Library Simple Complex Notes
typecheck.macro 46 105
ajv 108 331
io-ts 235
runtypes 357
zod 11471 zod throws an exception upon validation error, which resulted in this extreme case

Error Message Validator

Note: Out of all libraries, typecheck.macro has the most comprehensive error messages!

[Benchmarking is WIP]

Generate data with pnpm run bench:prep -- [simple|complex|complex2] and run a benchmark with pnpm run bench -- [macro|ajv|io-ts|runtypes|zod] --test=[simple|complex|complex2]

Caveats

  • typecheck.macro does not handle multi-file types. E.g if Foo imports Bar from another file, typecheck cannot generate a validator for it. registerType is file scoped.
    • If this is a significant problem, file a Github issue so I increase the priority of creating a CLI tool that can handle multi-file types.
  • typecheck.macro can intersect intersection types and intersection types with circular properties, but the following case is WIP: type Foo = {next: Foo} & {next : string}. In other words, you shouldn't intersect a circular property (like next) with another property. However, type Foo = {next: Foo} & {data: string} is totally fine.

Contributing

Read the contributor docs. Contributions are welcome and encouraged!

typecheck.macro's People

Contributors

vedantroy avatar heavenston avatar simnalamburt avatar

Stargazers

ThinkThinkSyn avatar 卧壑 avatar Marc Wieland avatar KangKang avatar Rintaro Itokawa avatar  avatar  avatar ruxxzebre avatar Jay Park avatar Anton Vlasov avatar 小健 avatar Sriram Velamur avatar Javed Khan avatar sam bacha avatar Nathan Hutchision avatar Hidetake Iwata avatar Nick Seagull avatar William Bert avatar Serdar Sayin avatar Denis Davydkov avatar Josscii avatar Fritz Blueford avatar David Gonzalez avatar Dinh Ngoc Hien avatar Ryota Murakami avatar Volodymyr Kartavyi avatar Pierre avatar Chew Chit Siang avatar Michal Dziekonski (mdz) avatar Yang Zhang avatar  avatar David Lee avatar Rod Kisten (Costa) avatar  avatar  avatar Vitali Vasilko avatar Fabien BERNARD avatar Konstantin Azizov avatar Russell Tepper avatar Alec Newman avatar Elliott Dicus avatar Joseph Avila Alvarez avatar Sungbin Jo (조성빈) avatar Cameron Gorrie avatar Juan Luis Paz avatar  avatar  avatar Monir Abu Hilal avatar Rakesh Harishbhai Purohit avatar a avatar Joey Figaro avatar Travis Arnold avatar Andrew Burgess avatar Jon Cardasis avatar Will Mruzek avatar Darko Bozhinovski avatar Arthur Denner avatar Nex Zhu avatar Fabio Dias Rollo avatar Landon avatar Brian Kent avatar Miaxos avatar Zeb Piasecki avatar meh. avatar  avatar  avatar  avatar Vadim Orekhov avatar Johan Holmerin avatar Jordan Brant Baker avatar Andrey avatar Dongmyeong Seo avatar Corvo Attano avatar Yacine Rezgui avatar Jamie Rolfs avatar  avatar Kareem Daggash avatar CHOSSY Lucas avatar Tanay Pingalkar avatar Tim Kendall avatar Matt Gilbride avatar Joel Gallant avatar Tyler W. Walch avatar Nic Vickery avatar Philip Viktor Schulz-Klingauf avatar  avatar Yuri van Geffen avatar Eero Tanskanen avatar Petros Angelatos avatar Mohamed Daahir avatar Steven Olsen avatar Tim Etler avatar Renato Ribeiro avatar Timofey Arkusha avatar Nicolas Rolland avatar Chris Santamaria avatar  avatar 노성호 avatar sim da-song avatar Michael Utz avatar

Watchers

James Cloos avatar  avatar  avatar  avatar  avatar  avatar

typecheck.macro's Issues

[Proposal] Support decoders

typecheck.macro now supports arbitrary runtime constraints. However, it does not yet support decoders, which can deserialize values. It's complicated to implement this because, we can't arbitrarily mutate the input. For example:

x => {
  // Obviously this doesn't affect the value of the passed in parameter 
  // outside of this function
  x = 3
}

We can still mutate fields in the input. But, we can't re-assign the input itself. This doesn't just apply to the passed in input. We also need to deal with the following issue:

for (let x of some_array) {
     // this won't affect the actual element
    // in the array
     x = 5   
}

Of course, we can solve this issue by using a numerical for-loop, instead of a for-each loop, but that only solves this particular case.

Does anyone have thoughts?

TSIndexedAccessType unexpected error

I have an autogenerated type (by graphql codegen) like this:

/** Specifies the fields required to create a new customer. */
export type CustomerCreateInput = {
  /** The customer’s first name. */
  readonly firstName?: Maybe<Scalars['String']>
  /** The customer’s last name. */
  readonly lastName?: Maybe<Scalars['String']>
  /** The customer’s email. */
  readonly email: Scalars['String']
  /**
   * A unique phone number for the customer.
   *
   * Formatted using E.164 standard. For example, _+16135551111_.
   */
  readonly phone?: Maybe<Scalars['String']>
  /** The login password used by the customer. */
  readonly password: Scalars['String']
  /** Indicates whether the customer has consented to be sent marketing material via email. */
  readonly acceptsMarketing?: Maybe<Scalars['Boolean']>
}

Then in the same file I add:

registerType('CustomerCreateInput')
export const isValidCustomerCreateInput = createValidator<CustomerCreateInput>()

And my app throws an error like this:

./models/shopify.ts
MacroError: <rootDir>/models/shopify.ts: Unexpected error because TSIndexedAccessType was not expected. Please report this to the developer.

Let me know if you need more information, and thanks for this library!

Syntax error when typing lambda parameters inside constraints

In a newly created CRA project, the following code

type NumberContainer = {
  pos: Positive;
}
type Positive = number;

registerType('NumberContainer')

export const x = createValidator<NumberContainer>(undefined, {
  constraints: {
    Positive: (x: Positive) => x > 0
  }
})

Fails with

SyntaxError: /Users/ducaale/lesson-editor/src/lessonValidation.ts: typecheck.macro: /Users/ducaale/lesson-editor/src/lessonValidation.ts: Unexpected token, expected "," (1:3)

> 1 | ((x: Positive) => x > 0)
    |    ^ Learn more: https://www.npmjs.com/package/typecheck.macro
This error occurred during the build time and cannot be dismissed.

Since I have strict mode enabled in my tsconfig.json, I can't leave x untyped and typing it would lead to syntax error from typecheck.macro. My workaround at the moment is to disable ts checking with ts-ignore

export const x = createValidator<NumberContainer>(undefined, {
  constraints: {
    // @ts-ignore
    Positive: x => x > 0
  }
})

Why Examples use typecheck.macro as node_module?

@vedantroy
Why examples use code of this repo?

  1. It increase the number of managing point.
    1. When the version of typecheck.macro updates, then the vesion of example dependency should be updated too.
  2. Memories are wasted because of duplicated resources.

In my opinion, It is better to use compiled source like vue or other repos?

Release "next" or preview versions

@vedantroy I would love to try out the latest additions in my project already, what do you think about releasing them in a next version already? Or do you have an estimate for the next "full" release?

Reduce usage of Object.prototype.hasOwnProperty

We use Object.prototype.hasOwnProperty to handle cases where the key is required and undefined is a possible value. (In that case, if object.key is undefined, it is impossible to tell whether the key exists without hasOwnProperty). However, we only need to use the method in that case.

We can improve performance/generated code size by not using Object.prototype.hasOwnProperty if undefined is not a possible value.

Have you promoted your project on social networks?

Hi there,

First of all, I find your project amazing. I'm very impressed by your work.
However, I'm saddened to see how little stars your project has gathered!

So I though that I could throw my 5 cents, since I also started an opensource project that is somewhat popular. I'd like to see this project become more popular, here's a few things I did for my own:

  • Make a logo, even a simple one
  • Publish a catchy article on Medium.com on how to validate data in a few seconds
    Here's a list of curators that will publish your article to all their subscribers:
    https://medium.com/the-innovation/top-10-independent-medium-publications-for-programming-article-80476988b10c
  • Publish a simple article on Dev.to where you explain the problem and show the solution
  • Once the above is done:
    • Publish the links to those articles on:
      • HackerNews (programming)
      • Reddit (typescript)
      • Lobste.rs (javascript)
      • ProductHunt (yes, it works)
  • You can contact a few top developers via Twitter to ask them their assessment, and maybe share your project.
  • Create a little website for your project.

I got my very first 800 stars this way.

Hope this will help you. Cheers

Support typeof tuple[number] pattern

// Use this to get an iterable of possible values.
const namingConvention = ['snake_case', 'camelCase'] as const;

type NamingConvention = typeof namingConvention[number];

registerType('NamingConvention');

export const isValidNamingConvention = createValidator<NamingConvention>();

got error:

Unexpected error because TSIndexedAccessType was not expected. Please report this to the developer.

Maintainers

See this

This library still needs a maintainer. Feel free to reach out if you want to take a shot at maintaining it.

Note, seems like proteriax is now helping out. But as always, more maintainers are appreciated. There is a pretty big backlog of features that could be implemented.

Update:

  • I have invited proteriax to have push access. If anyone else has wants commit access, let me know!

Implement named type function extraction

Right now

interface foo {
val: bar;
val2: bar;
}

will result in a function with validation code duplicated for val and val2. Even though, they are both the same type. We should be able to detect when there are multiple fields with the same named type, and extract that code into a common function, which we call from our function. This can reduce generated code size.

This is the first step towards implementing circular types support.

Support mapped types

Mapped types are listed as WIP. What is the status of these types?

  • Record
  • Required
  • Pick
  • Omit

[feature request] define constraints at the type-level

Sometimes, I have a type A that might be used in other types. If I need to add constraints on that type, I would have to repeat those constraints every time I create a validator for a type that references type A.

type Url = string

type Video = {
  url: Url
  length: number
}

type Image = {
  url: Url
  width: number
  height: number
}

registerType('Video')
register('Image')

const imageValidator = createValidator<Image>(undefined, {
  constraints: {
    Url: x => x.startsWith('http://') // this can't be moved to an object or babel-macro will complain
  }
})

const videoValidator = createValidator<Video>(undefined, {
  constraints: {
    Url: x => x.startsWith('http://')
  }
})

It would be nice if one of the following was possible:

  • Type constraints can be defined when registering the corresponding type

    type Url = string
    
    type Video = {
      url: Url
      length: number
    }
    
    type Image = {
      url: Url
      width: number
      height: number
    }
    
    registerType('Url', x => x.startsWith('http://'))
    registerType('Video')
    register('Image')
    
    const x = createValidator<Image>()
    const x = createValidator<Video>()
  • Type constraints can be defined globally

    type Url = string
    
    type Video = {
      url: Url
      length: number
    }
    
    type Image = {
      url: Url
      width: number
      height: number
    }
    
    registerType('Video')
    register('Image')
    
    registerConstraints({
      Url: x => x.startsWith('http://')
    })
    const x = createValidator<Image>()
    const x = createValidator<Video>()

[Proposal] Add arbitrary refinement/validation functions

I want to be able to have arbitrary refinement of types. For example:

type A = {
    val: string // want to verify the length is above 5
}

The hard part of this is how to let the user of the macro indicate which keys should be refined/validated, and with what functions.

For example, do we let the user just specify object keys that they want extra validation for? Or, should I implement a type DSL that allows users to specify, for example: input > next* > foo, which would mean "from the input, recursively traverse all .next keys and match all foo keys that are found along the way". This seems overly complicated, however, it would allow for arbitrary matching of keys that could be validated.

I also generally want to avoid DSLs.

Handle multi-file types

As suggested in the README, I'd like to express my wish for multi-file types support.

My use case is the validation of data for React components. The types are simple, but spread over many files because each component lives in a separate file and defines the type it expects as input. I only need to validate data for root (page) components, which contain a deep hierarchy of descendant components.

[Proposal] use class as a type in constructor

Is this feature is hard to impement?
If validator can be used with class in constructor, it would be better.

example

class A {
  a: number;
  b: string;
  constructor(a: A) {
    const vaildate = createValidator<A>();
    console.log('isVaild'+vaildate(a));
    this.a = a.a;
    this.b = a.b;
  }
}

new A({a: 1, b:'2'})

Currently, It makes BABEL_TRANSFORM_ERROR 😢

Unexpected error because Human-friendly type descriptions are not possible for circular types that are not objects or unions

Small example:

src/mytypes.ts:

import createValidator, { createDetailedValidator, registerType } from "typecheck.macro";

export type ProjectLayerConfig = {
  layers: Array<Group | Layer>;
};
registerType("ProjectLayerConfig");

type Item = {
  id: string;
  label: string;
  isVisible: boolean;
};
registerType("Item");

export type Group = Item & {
  type: "Group";
  children: Array<Group | Layer>;
};
registerType("Group");

export type Layer = Item & {
  type: "Layer";
  children: SubLayer[];
};
registerType("Layer");

export type SubLayer = Item & {
  type: "SubLayer"
};
registerType("SubLayer");

const validator = createDetailedValidator<ProjectLayerConfig>();

package.json:

{
  "name": "typescript-babel",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "compile": "babel src --out-dir dist --extensions \".ts\""
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@babel/cli": "^7.12.16",
    "@babel/core": "^7.12.16",
    "@babel/plugin-transform-modules-commonjs": "^7.12.13",
    "@babel/preset-typescript": "^7.12.16",
    "babel-plugin-macros": "^3.0.1",
    "typecheck.macro": "^0.7.1"
  }
}

Output:

> [email protected] compile /path/typescript-babel
> babel src --out-dir dist --extensions ".ts"

MacroError: /path/typescript-babel/src/mcve.ts: Unexpected error because Human-friendly type descriptions are not possible for circular types that are not objects or unions. Please contact me, I didn't know this was possible!. Please report this to the developer.
    at throwUnexpectedError (/path/typescript-babel/node_modules/typecheck.macro/dist/macro-assertions.js:91:9)
    at visitInstantiatedType (/path/typescript-babel/node_modules/typecheck.macro/dist/code-gen/irToHumanFriendlyDescription.js:116:49)
    at visitIR (/path/typescript-babel/node_modules/typecheck.macro/dist/code-gen/irToHumanFriendlyDescription.js:73:10)
    at /path/typescript-babel/node_modules/typecheck.macro/dist/code-gen/irToHumanFriendlyDescription.js:185:21
    at Array.map (<anonymous>)
    at visitObjectPattern (/path/typescript-babel/node_modules/typecheck.macro/dist/code-gen/irToHumanFriendlyDescription.js:183:22)
    at visitIR (/path/typescript-babel/node_modules/typecheck.macro/dist/code-gen/irToHumanFriendlyDescription.js:73:10)
    at humanFriendlyDescription (/path/typescript-babel/node_modules/typecheck.macro/dist/code-gen/irToHumanFriendlyDescription.js:24:10)
    at /path/typescript-babel/node_modules/typecheck.macro/dist/code-gen/irToInline.js:868:177
    at wrapFalsyExprWithErrorReporter (/path/typescript-babel/node_modules/typecheck.macro/dist/code-gen/irToInline.js:869:5) {
  name: 'MacroError',
  code: 'BABEL_TRANSFORM_ERROR'
}
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] compile: `babel src --out-dir dist --extensions ".ts"`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the [email protected] compile script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/amagee/.npm/_logs/2021-02-15T22_28_06_776Z-debug.log

Note: if I use createValidator instead of createDetailedValidator, it succeeds.

Handle multi-file types

Hi! Thx for your job, I try to use it with nextjs project to check types of functions that operate with server data, and I need to check comming data types. In my project typescript types (interfaces) are located in other files. How can I solve this problem or can you upgrade your lib?

Make code more concise for boolean operations

Rather than generating (for example) !(a === undefined), we should generate a !== undefined. This could be done by having a variable in the State interface that says whether the parent is negating the current expression. If they are, we can just inline the negation and signal the parent (maybe by adding another type to Validator) that we negated the expression.

Machine-readable errors

Thanks for the new error messages functionality, it's a big improvement over just false 😁

However actually trying to use the returned values is a bit inconvenient (beyond just dumping them to console or outputting them somewhere as-is) 😅

I am trying to generate something like a "diff", to highlight places where the validation failed in the input - so ideally I would need something like a structured representation of where the problems are, and then the raw expected type.

Instead of this:

const errors = [
    [
        "input["configuration"]["catalog"]["categories"][0]["item_color"]",
        undefined,
        "type: string"
    ],
    [
        // the final ["groups"] segment seems to be duplicated,
        // it doesn't exist in the type definition

        // not sure if that's something new or still WIP ;)
        "input["configuration"]["integrations"]["providers"]["example"]["quiz"]["groups"]["groups"]",
        undefined,
        "object↵  where all keys are: ↵    type: string"
    ]
]

I would like to get:

const errors = {
    configuration: {
        catalog: {
            categories: {
                0: {
                    item_color: {value: undefined, expectedType: 'string'}
                }
            }
        },
        integrations: {
            providers: {
                example: {
                    quiz: {
                        groups: {value: undefined, expectedType: '{ [key: string]: string; }'}
                    }
                }
            }
        }
    }
}

The structure transformation could be done afterwards, splitting the path string and transforming it into an object-structure or something. Maybe I could also generate type-definition strings from the IR if I request that as the return value (is that possible from the data we still have in that representation?)

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.