🦩 Joi like validations for TypeScript

Overview

🦩 Computed Types

Runtime validation types for TypeScript.

View On NPM Build Status Dependency Status Coverage Status License


Computed-Types (formerly: Funval) is a strongly-typed validation library for TypeScript. Using function interfaces, computed-types knows how to transform and validate your data, and automatically generates accurate TypeScript interfaces on compile time.

Using computed-types:

const UserSchema = Schema({
  name: string,
  amount: number,
  flags: array.of(string).optional();
});

type User = Type<typeof UserSchema>;

Equivalent code in Joi:

const UserSchema = Joi.object({
  name: Joi.string().required(),
  amount: Joi.number().required(),
  flags: Joi.array().items(Joi.string()),
});

type User = {
  name: string;
  amount: number;
  flags?: string[];
}

Main Features

  • Easy to Read - Uses runtime types like in TypeScript (including string, array, unknown, etc...)
  • Reduce Duplication - Create new validator using existing functions in seconds.
  • TypeScript Validation - Detect errors during compile time as well.
  • Function Composition - Chain multiple validators to generate new types.
  • Data Transformation - Combine validation and formatting in the one action.
  • Asynchronous & Synchronous Support - Automatically detected promises and async validation.
  • Zero Dependencies - Light and compact library.
  • Pure Javascript - Also works without TypeScript.

Table of Contents


Install

Node.js:

npm i computed-types

Deno:

import Schema, { Type, string, number, array } from 'https://denoporter.sirjosh.workers.dev/v1/deno.land/x/computed_types/src/index.ts';

Usage

import Schema, { Type, string, number, array } from 'computed-types';

const UserSchema = Schema({
 name: string.trim().normalize().between(3, 40).optional(),
 username: /^[a-z0-9]{3,10}$/,
 status: Schema.either('active' as const, 'suspended' as const),
 items: array
   .of({
     id: string,
     amount: number.gte(1).integer(),
   })
   .min(1),
});

type User = Type<typeof UserSchema>;
const validator = UserSchema.destruct();

const [err, user] = validator({
  username: 'john1',
  // 🚨 TypeScript Error: Type '"unregistered"' is not assignable to type '"active" | "suspended"'.
  status: 'unregistered',
  items: [{ id: 'item-1', amount: 20 }],
});

console.log(err);
// 🚨 ValidationError: Expect value to equal "suspended" {
//   errors: [
//     {
//       error: TypeError: Expect value to equal "suspended",
//       path: ['status']
//     }
//   ]
// }

Creating new Types

A computed type is any function that can return a value without throwing any exceptions. Creating a custom type allows you to normalize, transform and validate any input.

For example this type will validate email addresses:

import * as EmailValidator from 'email-validator';

function Email(input: unknown): string {
  if (!EmailValidator.validate(String(input))) {
    throw new TypeError(`Invalid email address: "${input}"`);
  }

  return input;
}

You can use the above validator on schemas as an Email type and it will validate inputs in the form of { email: unknown } to { email: string } type.

const UserSchema = {
  email: Email,
};

const validator = Schema(UserSchema);

To create optional types, change the validator arguments to optional as well:

function OptionalEmail(input?: unknown): string | undefined {
  return input == null ? undefined : Email(input);
}

This will validate inputs in the form of { email?: unknown } to { email: string | undefined }.


Using Transform

The custom Email validator above will not support validator chaining, but we can easily fix this by using the .transform() method.

const EmailWithValidatorChain = unknown.string.transform(Email)

I can now make use of the validator chain:

const UserSchema = {
  email: EmailWithValidatorChain.optional().max(100),
};

const validator = Schema(UserSchema);

Asynchronous Validators

Asynchronous validators are supported by returning a Promise (or PromiseLike) values:

import fetch from 'node-fetch';

async function AvailableUsername(input: string): Promise<string> {
  const res = await fetch(`/check-username?username=${encodeURIComponent(input)}`);

  if (!res.ok) {
    throw new TypeError(`Username "${input}" is already taken`);
  }

  return input;
}

Computed-types automatically detects promise and convert the return type of the Validator to promise as well:

const UserSchema = {
  username: AvailableUsername,
};
const validator = Schema(UserSchema);

const user = await validator({ username: 'test' });

Trying to access the return value without resolving it with promise first will detect and alert automatically via TypeScript on compile time.


Validators Chain

Every validator in "computed-types" is a validation function that can be called and validate any sort of data. In addition, each validator has a few helper methods to chain multiple validators together.

For example, check out this use case:

import { unknown } from 'computed-types';

const validator = unknown.number().gt(0).toFixed(2);

console.log(validator('123.4567')); // '123.46'

You can see here all the custom chain methods for each type. Please note that after calling toFixed, the validator no longer returns a number but a string so all the helpers functions available after toFixed will be the string helpers.

In addition the type helpers, each validator has those default chain helpers so use:


.equals()

Verify the return value equals to the given value.

const validator = boolean.equals(true);
.test()

Verify the return value pass the given test function.

import * as EmailValidator from 'email-validator';

const validator = string.test(EmailValidator.validate, 'Invalid email address');
.transform()

Transform the return value to a new value or throw to fail the validation process. The return value can be any value, including different types.

const validator = number.transform((x): number => {
  if (x <= 0) {
    throw new RangeError('Expected number to be positive');
  }

  return Math.sqrt(x);
});
.construct()

Similar to .transform() but less common. This helper is useful when you want to change the validator input before validating it. The returning value of the construct function should always return an array as this array will pass to the original validator input as arguments.

const validator = number.gt(1).construct((x: number, y: number) => [x + y]);
validators(x, y); // x + y
.optional()

Will convert the validator to an optional by allowing undefined or null values. This is very useful for parsing when creating optional properties on a schema.

const validator = Schema({
  name: string.trim().min(1),
  address: string.trim().optional(),
})
.strictOptional()

Same as .optional() but allows only undefined values.

const validator = Schema({
  name: string.trim().min(1),
  address: string.trim().optional(),
})
.destruct()

Use this as the final helper on the chain. It will catch any validation error and spread it to a 2-arguments array with an error and possible value on success. Useful if you don't like catching errors.

const validator = Schema({
  name: string.trim().min(1),
}).destruct();

const [err, user] = validator(req.body);
.error()

Will catch any error and replace it with your custom error instead. You can pass a string, Error or a function that will generate an error for you. Notice that on most cases you will not need to use this helpers, as most validation helpers has an optional error param with the same functionality.

const validator = Schema({
  name: string.error('expect input to be string'),
  amount: number.gt(0, (val) => `${val} is not positive amount`);
});

Available Types

It's useful to import the following native types when building custom schemas. Click on each type to see some validation examples.

import Schema, { unknown, string, number, boolean, array, DateType } from 'computed-types';


Schema

Create a validator from schema object, values or function validators.

const validator = Schema({
  name: string,
  amount: number,
}, 'Missing name or amount');
Strict mode

By default, the schema validator will ignore all properties that aren't exist on the schema. If you want to throw an error instead you can toggle the strict mode on.

const validator = Schema({
  name: string,
  amount: number,
}, { strict: true });
Schema.either

Works as OR switch. Create a validator from multiple function validators or schema objects.

const validator = Schema.either({ foo: string}, { bar: number });
// validate: { foo: string; } | { bar: number; }
Schema.merge

Works as AND switch. Create a validator from multiple function validators or schema objects.

const validator = Schema.merge({ foo: string}, { bar: number });
// validate: { 
//   foo: string;
//   bar: number; 
// }
Schema.enum

Create a validator from TypeScript enum.

enum Status {
 OK,
 Invalid,
};

const validator = Schema.enum(Status, 'Invalid status');

unknown

Accept any unknown value:

const validator = Schema({
  data: unknown,
});
unknown.schema()

Accept any value as an input and try to convert it the given schema:

const validator = unknown.schema({
  foo: string.trim(),
})
unknown.object()

Accept any value as an input and try to convert it to an object:

const validator = unknown.object('Expect data to be an object');
unknown.array()

Accept any value as an input and try to convert it to an array:

const validator = unknown.array().min(1).of(boolean);
unknown.string()

Accept any value as an input and try to convert it to a string:

const validator = unknown.string('Expect data to be string').toUpperCase();

// will accept: `{ data: 1 }` and convert it to `{ data: '1' }`
// will throw: `{ data: null }`
unknown.number()

Accept any value as an input and try to convert it to a number:

const validator = unknown.number('Expect data to be number').gt(0);
unknown.boolean()

Accept any value as an input and try to convert it to a boolean:

const validator = unknown.boolean('Expect data to be boolean').equals(true);
unknown.date()

Accept any value as an input and try to convert it to a date:

const validator = unknown.date('Expect data to be date').equals('1970-01-01T00:00:00.050Z');
unknown.enum()

Accept any value as an input and try to convert it to the given enum:

enum Status {
  OK,
  Invalid,
}

const validator = unknown.enum(Status);

string

Accept only string values (including empty strings).

const validator = Schema({
  content: string,
});
string.toLowerCase()

Accept string and convert it to lower case.

const validator = string.toLowerCase().trim();
string.toUpperCase()

Accept string and convert it to upper case.

const validator = string.toUpperCase().trim();
string.toLocaleLowerCase()

Accept string and convert it to local lower case.

const validator = string.toLocaleLowerCase('en-US').trim();
string.toLocaleUpperCase()

Accept string and convert it to local upper case.

const validator = string.toLocaleUpperCase('en-US').trim();
string.trim()

Accept string and trim it.

const validator = string.trim();
string.normalize()

Accept string and normalize it.

const validator = string.normalize();
string.min()

Accept string with minimum given length.

const validator = string.min(2).toLowerCase();
string.max()

Accept string with maximum given length.

const validator = string.max(10).toUpperCase();
string.between()

Accept string within the given length range.

const validator = string.between(2, 10).trim();
string.regexp()

Accept only strings that match the given regular expression.

const validator = string.regexp(/^Hello/).trim();

number

Accept only number type values.

const validator = Schema({
  amount: number
});
number.float()

Accept only floating numbers (throws on NaN or non-finite values).

const validator = number.float().gt(0);
number.integer()

Accept only integer numbers.

const validator = number.integer().gt(0);
number.toExponential()

Accept number and convert it to exponential format string.

const validator = number.toExponential().toUpperCase();
number.toFixed()

Accept number and convert it to fixed format string.

const validator = number.toFixed(3);
number.toLocaleString()

Accept number and convert it to locale string.

const validator = number.toLocaleString('en-US');
number.toPrecision()

Accept number and convert it to precision string.

const validator = number.toPrecision(2);
number.toString()

Accept number and convert it to string.

const validator = number.toString(16).toUpperCase();
number.gte()

Accept number that greater or equal than the boundary given.

const validator = number.gte(1.5);
number.lte()

Accept number that lower or equal than the boundary given.

const validator = number.lte(10.5);
number.gt()

Accept number that greater than the boundary given.

const validator = number.gt(1.5);
number.lt()

Accept number that lower than the boundary given.

const validator = number.lt(10.5);
number.between()

Accept number between the given boundaries.

const validator = number.between(0, 1);

boolean

Accept only boolean type values.

const validator = Schema({
  agree: boolean
});

array

Accept only array type values.

const validator = Schema({
  agree: array
});

##### `array.of()`

Accept only array with given items.

```ts
const numbers = array.of(number); // numbers[]
const tuple = array.of(number).between(1, 2); // [number, number?]
const objects = array.of({ foo: number }); // { foo: number }[]
const enums = array.of(Schema.enum(Status); // Status[]
array.min()

Accept only array with minimum given items.

const validator = array.min(2);
array.max()

Accept only array with maximum given items.

const validator = array.max(10);
array.between()

Accept only array with minimum and maximum count of items.

const validator = array.between(2, 10);

DateType

Accept only instances of Date.

const validator = Schema({
  eventTime: DateType
});
DateType.toISOString()

Accept Date and convert it to ISO date string.

const validator = DateType.toISOString();
DateType.getTime()

Accept Date and convert it to a timestamp.

const validator = DateType.getTime().gt(100);
DateType.gte()

Accept Date that greater or equal than the boundary given.

const validator = DateType.gte(new Date('2020-10-01T10:00:00.000Z'));
DateType.lte()

Accept Date that lower or equal than the boundary given.

const validator = DateType.lte(new Date('2020-10-01T10:00:00.000Z'));
DateType.gt()

Accept Date that greater than the boundary given.

const validator = DateType.gt(new Date('2020-10-01T10:00:00.000Z'));
DateType.lt()

Accept Date that lower than the boundary given.

const validator = DateType.lt(new Date('2020-10-01T10:00:00.000Z'));
DateType.between()

Accept Date between the given boundaries.

const validator = DateType.between(new Date('2020-09-01T10:00:00.000Z'), new Date('2020-10-01T10:00:00.000Z'));

License

MIT license © 2020 Neuledge

Comments
  • Objects with unknown property names

    Objects with unknown property names

    Very often I'll encounter situations where I need to type an object that has unknown property names (e.g. an id or a key) but the shape of its value is known.

    e.g.

    type Point = { x: number; y: number};
    interface PointMap {
      [key: string]: Point;
    }
    

    I can't really figure out how to accomplish that with computed-types.

    enhancement 
    opened by b-zurg 10
  • Optional properties with TypeScript

    Optional properties with TypeScript "strictNullChecks": false

    All properties in type from schema are optional. Example below.

    image

    Readme shows that only the flags property should be optional.

    Reproduce:

    TS: 4.6.3 with the following settings:

     "strictNullChecks": false
    

    https://codesandbox.io/s/cool-visvesvaraya-dpubbe?file=/src/index.ts

    invalid awaiting feedback 
    opened by stpoa 7
  • SchemaInput makes shared union type optional

    SchemaInput makes shared union type optional

    I'm using computed-types 1.3.0. My use case is defining a schema where users can enter one or many of the following, and as per the suggestion in #51, upon using SchemaInput it marks the id properties as optional despite them being required. Is there any workaround for this?

    import Schema, { SchemaInput, string } from "computed-types";
    
    const A = Schema.either({ id: 1 as const }, { id: 2 as const, data: string.optional() });
    type AType = SchemaInput<typeof A>;
    

    image

    opened by SirJosh3917 6
  • Forbid unknown properties (Exact type) support

    Forbid unknown properties (Exact type) support

    Hi, just found this project and played around, thank you for the nice work!

    In Joi, if you have not-in-schema properties in your validation target, Joi will return error, which I found convenient, but in this project it seems there is not such feature. I wonder if this is in the plan of this project, or is it already supported?

    Example Joi.validate() return error:

    {
       "message": "\"unknown_prop\" is not allowed"
    }
    

    Thanks in advance.

    help wanted 
    opened by iamlockon 6
  • Added funval to typescript-runtime-type-benchmarks

    Added funval to typescript-runtime-type-benchmarks

    Hey @moshest,

    Great take on the validator!

    I added your project to the benchmarking repo: https://github.com/moltar/typescript-runtime-type-benchmarks

    opened by moltar 5
  • Validating optional fields are defined

    Validating optional fields are defined

    I'm enjoying this library. However, I'm struggling with the following...

    If I have a type with an optional field, and an instance of that type for which I want to validate the optional field is populated, I'm doing like:

          type MyType = { foo?: string }  // optional field foo
          const myObject: MyType = {foo: "bar"}  // foo is populated, so I want this to validate
    
          const schema = Schema({
            foo: string
          })
    
          const validator = schema.destruct()
    
          const [ err, validatedObject ] = validator(myObject)
    

    However, this gives an error on applying validator(myObject):

    Argument of type 'MyType' is not assignable to parameter of type '{ foo: string; }'.
      Types of property 'foo' are incompatible.
        Type 'string | undefined' is not assignable to type 'string'.
          Type 'undefined' is not assignable to type 'string'.ts(2345)
    

    I also tried using unknown like:

          const schema = Schema({
            foo: unknown.string()
          })
    

    hoping that this might be more tolerant to the input type being undefined, but it's the same error.

    Is there a way to do this?

    opened by BryceCicada 4
  • ❓ strong type for validator used in functions and `tsd` test replacements

    ❓ strong type for validator used in functions and `tsd` test replacements

    New to use of the project; moving from use of tsd. I'd like to pass validators to various functions for testing. For example:

    type ValidatorType = unknown; // FIXME: needs a specific type
    const isValidType = (valid: ValidatorType, value: unknown) => {
    	const [err, value] = (valid as any).destruct()(value);
    	// ...
    };
    

    I'm unable to construct a defined type for ValidatorType.

    I'm also interested in what the computed-types idiom replacing these tsd tests would be...

    //...
    expectType<typeof xdgAppPaths>(xdgAppPaths);
    expectType<typeof xdgAppPaths>(xdgAppPaths());
    expectType<string | undefined>(paths.runtime({ isolated: false }));
    expectType<string>(paths.state());
    expectType<readonly string[]>(paths.configDirs());
    //...
    

    Any suggestions? Thanks.

    opened by rivy 4
  • Issue on Asynchronous Validators with Deno

    Issue on Asynchronous Validators with Deno

    Hi,

    I've found an issue with asynchronous validators.

    For example, correct behavior like this:

    import Schema, { string } from 'https://denoporter.sirjosh.workers.dev/v1/deno.land/x/computed_types/src/index.ts'
    const UserSchema = {
        username: string.trim().normalize().between(7, 255),
    };
    const validator = Schema(UserSchema);
    try {
        const user = await validator({ username: 'test' });
        console.log(user)
    } catch (error) {
        console.log(error)
        console.log(error.errors[0])
        console.log(error.errors[0]?.error?.message)
        console.log(error.errors[0]?.path)
    }
    

    Print this in console, wich is ok:

    ValidationError: username: Expect length to be between 7 and 255 characters (actual: 4)
        at toError (errors.ts:33:16)
        at createValidationError (errors.ts:51:34)
        at compiler.ts:102:23
        at test.ts:14:24
    {
      error: RangeError: Expect length to be between 7 and 255 characters (actual: 4)
        at string.ts:36:35
        at toError (errors.ts:36:24)
        at validations.ts:25:19
        at Validator.ts:41:24
        at Array.<anonymous> (compiler.ts:58:40)
        at compiler.ts:96:37
        at test.ts:14:24,
      path: [ "username" ]
    }
    Expect length to be between 7 and 255 characters (actual: 4)
    [ "username" ]
    

    But with async like this:

    import Schema, { string } from 'https://denoporter.sirjosh.workers.dev/v1/deno.land/x/computed_types/src/index.ts'
    async function AvailableUsername(input: string) {
        throw new TypeError('my error')
    }
    const UserSchema = {
        username: AvailableUsername,
    };
    const validator = Schema(UserSchema);
    try {
        const user = await validator({ username: 'test' });
        console.log(user)
    } catch (error) {
        console.log(error)
        console.log(error.errors[0])
        console.log(error.errors[0]?.error?.message)
        console.log(error.errors[0]?.path)
    }
    

    Return this:

    ValidationError: undefined
        at toError (errors.ts:33:16)
        at createValidationError (errors.ts:51:34)
        at compiler.ts:108:23
        at async test.ts:14:18
    TypeError: my error
        at AvailableUsername (test.ts:4:11)
        at Array.<anonymous> (compiler.ts:58:40)
        at compiler.ts:96:37
        at test.ts:14:24
    undefined
    undefined
    

    So asynchronous validators does not seems to work well with Deno :/

    Any idea?

    Thanks a lot!

    awaiting feedback 
    opened by KirianCaumes 4
  • Make optional() properties optional in typescript

    Make optional() properties optional in typescript

    Currently, when I make a property optional in the schema it outputs the type as undefined - which is not quite the same as optional.

    export const PersonSchema = Schema({
      name: string.optional(),
    })
    
    export type SourceMetric = Type<typeof PersonSchema>
    
    const obj: MySchema {} // this would error, missing name
    
    const obj: MySchema {
      name: undefined // allowed
    }
    

    As per this comment, it's fairly easy to add a utility type function that can convert undefined to optional. I think it would make sense to apply this by default as part of Type.

    https://github.com/Microsoft/TypeScript/issues/12400#issuecomment-502345531

    opened by calummoore 4
  • Validating nested objects

    Validating nested objects

    I'm having issues validating nested objects. There are no examples how to do it. I tried the following two approaches:

    const validator = Schema({
      foo: {
        bar: string.min(1)
      }
    })
    
    validator({ foo: { bar: "" } }) // <- error path is ["foo", "foo", "bar"]
    
    const validator = Schema({
      foo: Schema({
        bar: string.min(1)
      })
    })
    
    validator({ foo: { bar: "" } }) // <- error path is ["foo", "bar"]
    

    The first case looks like a bug.

    opened by wereHamster 3
  • Comments gets lost when I extend it with Type

    Comments gets lost when I extend it with Type

    export const DelLogReqQuerySchema = Schema({
        /** delete id, or delete all logs.*/
        id: string.optional(),
    })
    export interface IDelLogReqQuery extends Type<typeof DelLogReqQuerySchema> {}
    

    I use automation to create swagger doc. Let's say I use IDelLogReqQuery interface for documentation. Before minor version 11, I can see comments for id prop but that is no longer the case after minor version 11.

    I used https://github.com/YousefED/typescript-json-schema to read the type.

    opened by chwon-ezstrading 3
  • Update all development npm dependencies (2022-11-27)

    Update all development npm dependencies (2022-11-27)

    This is your weekly update of all development npm dependencies. Please take a good look at what changed and the test results before merging this pull request.

    What changed?

    ✳️ @typescript-eslint/eslint-plugin (5.42.1 → 5.44.0, minor) · Repo · Changelog · Release · Diff

    ✳️ @typescript-eslint/parser (5.42.1 → 5.44.0, minor) · Repo · Changelog · Release · Diff

    ✳️ eslint (8.27.0 → 8.28.0, minor) · Repo · Changelog · Release · Diff

    ✳️ prettier (2.7.1 → 2.8.0, minor) · Repo · Changelog · Release · Diff

    ✳️ typescript (4.8.4 → 4.9.3, minor) · Repo · Diff


    Depfu Status

    Depfu will only send you the next scheduled PR once you merge or close this one.

    All Depfu comment commands
    @​depfu refresh
    Rebases against your default branch and redoes this update
    @​depfu recreate
    Recreates this PR, overwriting any edits that you've made to it
    @​depfu merge
    Merges this PR once your tests are passing and conflicts are resolved
    @​depfu close
    Closes this PR and deletes the branch
    @​depfu reopen
    Restores the branch and reopens this PR (if it's closed)
    depfu 
    opened by depfu[bot] 0
  • Confusing error messages from `either`

    Confusing error messages from `either`

    node: 16.14.0 typescript: 4.5.5 computed-types: 1.11.2

          const schema = Schema.either(
            {
              foo: string.equals('X'),
              bar: array.max(0),
              baz: array.min(1),
            },
            {
              foo: string.equals('Y'),
              bar: array.min(1),
              baz: array.max(0),        
            },
          )
    
          const obj = { foo: 'Y', bar: [], baz: [] }
    
          const validator = schema.destruct()
    
          const [err, validatedObj] = validator(obj)
    

    The error message above is bar: Expect array to be minimum of 1 items (actual: 0). This makes sense for the value of foo === 'Y'.

    Here's a curious observation... If I use const obj = { foo: 'X', bar: [], baz: [] }, then the error changes to foo: Expect value to equal "Y". Two notes:

    1. I think that the error is harder to understand since the error reports the path foo to be wrong, when arguably it's the baz array that's not matching the schema.
    2. The error differs from the case given in the initial snippet. However, the cases are symmetric about X and Y (apart from the order in the either definition), so one might expect the errors to be similar.

    Obviously, the analogous error occurs if I swap the order of the schemas in the either definition.

    This is really pedantic, I know. The library is great and hopefully this is just some useful feedback for thought.

    opened by ChrisNixTriller 1
  • `.min` or `.destruct` on `.construct`ed fall back to main type

    `.min` or `.destruct` on `.construct`ed fall back to main type

    Hey there again, sorry that I keep digging up weird stuff :) I have created a constructed type with

    const stringOrObjectWithKey = string.construct(
      (arg: { key: string } | string) =>
        typeof arg === "string" ? [arg] : [arg.key]
    );
    

    and I can call that just fine like

    stringOrObjectWithKey("asd");
    stringOrObjectWithKey({ key: "asd" });
    

    now, when I chain that like

    const withMin = stringOrObjectWithKey.min(1);
    

    I lose the ability to call it with the overload:

    withMin("asd");
    // Argument of type '{ key: string; }' is not assignable to parameter of type 'string'.ts(2345)
    withMin({ key: "asd" });
    

    Looking at the types, I don't really see what is wrong there - and to be honest, these types are quite complicated to get started with so I'm a bit hesitant to do a PR on this.

    I have, though, created a type test for this that you can use to verify this behaviour: CodeSandBox

    I hope that helps to pinpoint it down.

    Please don't hesitate to contact me if I can help any further :slightly_smiling_face:

    /*eslint-disable @typescript-eslint/no-unused-vars */
    
    import Schema, { string } from "computed-types";
    import { SchemaResolveType, SchemaInput } from "computed-types/lib/schema/io";
    
    function expectType<T>(t: T) {
      return t;
    }
    function expectAssignable<Subtype, _Type extends Subtype>() {}
    
    const stringOrObjectWithKey = string.construct(
      (arg: { key: string } | string) =>
        typeof arg === "string" ? [arg] : [arg.key]
    );
    
    stringOrObjectWithKey("asd");
    stringOrObjectWithKey({ key: "asd" });
    expectType<(v: string | { key: string }) => string>(stringOrObjectWithKey);
    expectAssignable<
      string | { key: string },
      SchemaInput<typeof stringOrObjectWithKey>
    >();
    expectAssignable<
      SchemaInput<typeof stringOrObjectWithKey>,
      string | { key: string }
    >();
    
    const destructed = stringOrObjectWithKey.destruct();
    destructed("asd");
    destructed({ key: "asd" });
    expectType<(v: string | { key: string }) => [any, string?]>(destructed);
    // second signature missing here
    expectType<(v: string) => [any, string?]>(destructed);
    expectAssignable<string | { key: string }, SchemaInput<typeof destructed>>();
    expectAssignable<SchemaInput<typeof destructed>, string | { key: string }>();
    expectAssignable<[any, string?], SchemaResolveType<typeof destructed>>();
    expectAssignable<SchemaResolveType<typeof destructed>, [any, string?]>();
    
    const withMin = stringOrObjectWithKey.min(1);
    withMin("asd");
    withMin({ key: "asd" });
    expectType<(v: string | { key: string }) => string>(withMin);
    // second signature missing here
    expectType<(v: string) => string>(withMin);
    expectAssignable<string | { key: string }, SchemaInput<typeof withMin>>();
    expectAssignable<SchemaInput<typeof withMin>, string | { key: string }>();
    expectAssignable<string, SchemaResolveType<typeof withMin>>();
    expectAssignable<SchemaResolveType<typeof withMin>, string>();
    
    const inSchema = Schema({ test: stringOrObjectWithKey });
    inSchema({ test: "asd" });
    inSchema({ test: { key: "asd" } });
    expectType<(v: { test: string | { key: string } }) => { test: string }>(
      inSchema
    );
    expectAssignable<
      { test: string | { key: string } },
      SchemaInput<typeof inSchema>
    >();
    expectAssignable<
      SchemaInput<typeof inSchema>,
      { test: string | { key: string } }
    >();
    
    const destructedSchema = inSchema.destruct();
    destructedSchema({ test: "asd" });
    destructedSchema({ test: { key: "asd" } });
    expectType<(v: { test: string | { key: string } }) => [any, { test: string }?]>(
      destructedSchema
    );
    
    const mergedSchema = Schema.merge({ other: string.optional() }, inSchema);
    mergedSchema({ test: "asd" });
    mergedSchema({ test: { key: "asd" } });
    expectType<(v: { test: string | { key: string } }) => { test: string }>(
      mergedSchema
    );
    expectAssignable<
      { test: string | { key: string } },
      SchemaInput<typeof mergedSchema>
    >();
    expectAssignable<
      SchemaInput<typeof mergedSchema>,
      { test: string | { key: string } }
    >();
    
    const mergedDestructedSchema = mergedSchema.destruct();
    mergedDestructedSchema({ test: "asd" });
    mergedDestructedSchema({ test: { key: "asd" } });
    expectType<(v: { test: string | { key: string } }) => [any, { test: string }?]>(
      mergedDestructedSchema
    );
    
    
    opened by phryneas 7
  • Support set Schema output/input types explicitly

    Support set Schema output/input types explicitly

    In cases we already have the schema types defined, it would be a nice to have the Schema receive the output type and optionally the input type explicitly.

    Motivation: Validate that the validator actually implement the given types exactly.

    For example:

    type Person {
      name: string;
      age: number;
    }
    
    type PersonInput {
      name: unknown;
      age: unknown;
    }
    
    const PersonSchema = Schema<Person, PersonInput>({
      name: unknown.string(),
      age: unknown.number(),
    );
    
    // typeof PersonSchema == (input: PersonInput) => Person;
    

    I tried to implement it from the beginning, but I couldn't find a way to make it work nicely without breaking some functionality. Maybe it's time to give it another try.

    Considerations:

    • [ ] Support Schema without any type arguments
    • [ ] Support giving only output type
    • [ ] Support giving both output and input types
    • [ ] Support async validators
    • [ ] Bonus: Support IDE when Ctrl+Click on scheme field (ie. clicking on schema age: will jump to Person['age'])
    enhancement help wanted 
    opened by moshest 0
  • Adding closure support for circular types

    Adding closure support for circular types

    hi :smiley_cat:

    i was getting amongst computed-types for a side project i'm playing with, and then i realized the types i want to validate are circular, e.g. a tree data structure. i was wondering if it might be possible to use computed-types with circular types.

    i made a simplified example to show what i mean, and what i have working so far: https://repl.it/talk/share/circular-computed-types/43342

    // mod.ts
    
    import Schema, { Type, string, array } from 'https://denoporter.sirjosh.workers.dev/v1/deno.land/x/computed_types/src/index.ts'
    
    // lazy due to circular evaluation
    let _NodeSchema: any = null
    export const NodeSchema: any = function (...args: Array<any>): any {
      if (_NodeSchema == null) throw new Error('programmer error')
      return _NodeSchema(...args)
    }
    
    export type Node = Branch | Leaf
    
    export const BranchSchema = Schema({
      name: string.trim().normalize(),
      nodes: array.of(NodeSchema),
    })
    
    export type Branch = Type<typeof BranchSchema>
    
    export const LeafSchema = Schema({
      name: string.trim().normalize()
    })
    
    export type Leaf = Type<typeof LeafSchema>
    
    _NodeSchema = Schema.either(BranchSchema, LeafSchema)
    
    import { NodeSchema } from './mod.ts'
    
    const node = NodeSchema({
      name: 'a',
      nodes: [
        {
          name: 'b'
        },
        {
          name: 'c',
          nodes: [
            {
              name: 'd',
              nodes: [
                {
                  name: 'e'
                }
              ]
            },
            {
              name: 'f'
            }
          ]
        }
      ]
    })
    
    console.log(JSON.stringify(node, null, 2))
    

    i'm able to get the runtime to work with a silly hack, but i'm stuck on getting the types to work.

    was wondering, is this something that might be possible to do?

    cheers! :purple_heart:

    enhancement help wanted 
    opened by ahdinosaur 7
Owner
Neuledge
The first programmable knowledge base.
Neuledge
🪄 Multi step forms with in built validations

react-wizardry is a data-driven smart wizard component for creating powerful forms with in built validations. Demo Features ⚡ Data driven API ✅ In bui

Prabhu Murthy 38 Aug 16, 2022
Jquery.iocurve - jQuery plugin like Tone Curve on Photoshop or GIMP

jquery.iocurve jQuery plugin like Tone Curve on Photoshop or GIMP. See Official page for more information. Quick start Create HTML and open in your br

null 5 Jul 28, 2022
Simple translation for your javascripts, yummy with your favorite templates engine like EJS.

jsperanto Simple translation for your javascripts, yummy with your favorite templates engine like EJS. Pluralization, interpolation & "nested lookup"

Jean-Philippe Joyal 62 Oct 21, 2021
🎉🎉🎉like vcsode, string/texts can be replaced in file(s).

tiny-replace-files Like vscode, simple utility to quickly replace text in one or more files. ?? Getting Started install # npm npm install --save tiny

兔子先生 16 Nov 22, 2022
What does the Cosmos Hub validator set looks like without ICF delegations?

This is a Next.js project bootstrapped with create-next-app. Getting Started First, run the development server: npm run dev # or yarn dev Open http://

Made in Block 2 Sep 2, 2022
A simple and composable way to validate data in JavaScript (and TypeScript).

A simple and composable way to validate data in JavaScript (and TypeScript). Usage • Why? • Principles • Demo • Examples • Documentation Superstruct m

Ian Storm Taylor 6.3k Jan 9, 2023
Super Fast Complex Object Validator for Javascript(& Typescript).

Super Fast Object Validator for Javascript(& Typescript). Safen supports the syntax similar to the type script interface. This makes it easy to create

Changwan Jun 31 Nov 25, 2022
TypeScript-first schema validation for h3 and Nuxt applications

h3-zod Validate h3 and Nuxt 3 requests using zod schema's. Install npm install h3-zod Usage import { createServer } from 'http' import { createApp } f

Robert Soriano 48 Dec 28, 2022
Grupprojekt för kurserna 'Javascript med Ramverk' och 'Agil Utveckling'

JavaScript-med-Ramverk-Laboration-3 Grupprojektet för kurserna Javascript med Ramverk och Agil Utveckling. Utvecklingsguide För information om hur utv

Svante Jonsson IT-Högskolan 3 May 18, 2022
A joi extension to validate MongoDB's ObjectIDs

@marsup/joi-objectid This is a simple joi extension to validate MongoDB's ObjectIDs. Installation npm install --save @marsup/joi-objectid Usage const

Nicolas Morel 2 Dec 15, 2022
Hemsida för personer i Sverige som kan och vill erbjuda boende till människor på flykt

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

null 4 May 3, 2022
Prisma 2+ generator to emit Joi schemas from your Prisma schema

Prisma Joi Generator Automatically generate Joi schemas from your Prisma Schema, and use them to validate your API endpoints or any other use you have

Omar Dulaimi 26 Dec 24, 2022
Kurs-repo för kursen Webbserver och Databaser

Webbserver och databaser This repository is meant for CME students to access exercises and codealongs that happen throughout the course. I hope you wi

null 14 Jan 3, 2023
Simple and basic javascript form validations

JavaScript-Form-Validations Simple and basic javascript form validations Table of Validations: S. No. Type of validation Link 1 Non-empty text field h

MAINAK CHAUDHURI 23 Dec 17, 2022
🪄 Multi step forms with in built validations

react-wizardry is a data-driven smart wizard component for creating powerful forms with in built validations. Demo Features ⚡ Data driven API ✅ In bui

Prabhu Murthy 38 Aug 16, 2022
Node.js package with a customized HTTP and HTTPS agents to prevent SSRF with hosts validations and custom DNS feature.

http-agent-dns This is a Node.js package with a customized HTTP and HTTPS agents to prevent SSRF with hosts validations with a possibility to use a cu

Bruno Germano 4 Jul 21, 2022
A work-in-progress HTML sanitizer that strives for: performance like window.Sanitizer, readiness like DOMPurify, and ability to run in a WebWorker like neither of those.

Amuchina A work-in-progress HTML sanitizer that strives for: performance like window.Sanitizer, readiness like DOMPurify, and ability to run in a WebW

Fabio Spampinato 9 Sep 17, 2022
1KB lightweight, fast & powerful JavaScript templating engine with zero dependencies. Compatible with server-side environments like node.js, module loaders like RequireJS and all web browsers.

JavaScript Templates Contents Demo Description Usage Client-side Server-side Requirements API tmpl() function Templates cache Output encoding Local he

Sebastian Tschan 1.7k Jan 3, 2023
null 147 Dec 8, 2022
A cross-platform AutoHotKey-like thing with TypeScript as its scripting language

suchibot A cross-platform AutoHotKey-like thing with JavaScript/TypeScript as its scripting language. Built on top of uiohook-napi and nut.js. Install

Lily Scott 79 Sep 21, 2022