Series of TypeScript Compiler based Libraries
- I made 1,000x faster TypeScript validator library
- I found 10,000x faster TypeScript Validator Library
- [Typia] 15,000x faster validator and its histories
- I made 10x faster JSON.stringify() functions, even type safe
- Do not use class-validator and class-transformer, but use pure TypeScript type instead
- Automatic React components generator from TypeScript type
In nowaydays, TypeBox author has visited my typescript-json repository and sent a pull request containing schemas of TypeBox for benchmark. After merging the pull request and proceeding the benchmark, I'm very surprised by its amazing performance. TypeBox is faster than my library typescript-json in the is()
function benchmark and its speed is even 10,000x times faster than class-validator.
Someone can doubt objectivity of the benchmark. Therefore, to ensure the objectivity, I disclose all code used in the benchmark. Below codes are being used in the benchmark program and you also can run the benchmark program just by running the
npm install && npm run benchmark
commands after downloading typescript-json.Also, title of before article was I made 1,000x faster TypeScript validator library, but today's benchmark is showing that typescript-json is maximum 6,500x times faster. It's just because I added a new library class-validator in the benchmark and it is much slower than previous zod. Of course, I've continuously tuned the performance, but it just improved the performance about 2x times only.
Looking at the benchmark graph, TypeBox fails on some types like ObjectUnionImplicit
, but it is originated just by limitations of JSON schema spec, that is utilized by TypeBox.
Additionally, if additionalProperties: false
option which does not allow superfluous properties being used, TypeBox does not fail on such complicate union type (in typescript-json, it is called equals()
function). I can't sure that using such additionalProperties: false
option is an adequate solution for union type, but anway, another validator libraries never can provide such alternative solution.
Also, in the equals()
function (or additionalProperties: false
option), TypeBox is faster than my typescript-json as the is()
function was. Looking at those benchmarks, I'm really impressed and have curious how TypeBox can be such faster even overcome AOT (Ahead of Time) compilation of typescript-json.
How it such fast
AOT (Ahead of Time) and JIT (Just in Time) compilations.
In the previous article I made 10x faster JSON.stringify() functions, even type safe, I described that typescript-json could be faster than other validator libaries by utilizing AOT (Ahead of Time) compilation. TypeBox is also similar, but detailed skill is a little bit diffrent; JIT (Just in Time) compilation.
In the typescript-json case, it analyzes your source code in the compilation level and generates optimized validation code for the target type (ObjectSimple
). Therefore, validation code is generated in the compilation level and it is called the AOT compilation.
Otherwise TypeBox case, it analyzes schema object what you've defined and generates optimized validation code for the target type (TypeBoxObjectSimple
) in the runtime level. Therefore, validation code is generated in the runtime level and it is called the JIT compilation.
That is, generating optimized validation code is the reaon why typescript-json and TypeBox could be much faster than other validator libraries.
//-------------------------------------------
// SCHEMA OF TYPESCRIPT-JSON
//-------------------------------------------
import TSON from "typescript-json";
export type ObjectSimple = ObjectSimple.IBox3D;
export namespace ObjectSimple {
export interface IBox3D {
scale: IPoint3D;
position: IPoint3D;
rotate: IPoint3D;
pivot: IPoint3D;
}
export interface IPoint3D {
x: number;
y: number;
z: number;
}
}
TSON.is<ObjectSimple>(input);
//-------------------------------------------
// SCHEMA OF TYPEBOX
//-------------------------------------------
import { Type } from "@sinclair/typebox";
import { TypeCompiler } from "@sinclair/typebox/compiler";
const Point3D = Type.Object({
x: Type.Number(),
y: Type.Number(),
z: Type.Number(),
});
const Box3D = Type.Object({
scale: Point3D,
position: Point3D,
rotate: Point3D,
pivot: Point3D,
});
const TypeBoxObjectSimple = TypeCompiler.Compile(Box3D);
TypeBoxObjectSimple.Check(input);
Also, the reason why TypeBox is faster than typescript-json is based on how define the data schema.
Looking at the below code, than you may understand which different makes such performance gap. As you can see, typescript-json generates an independent validation function whenever new object type comes, but TypeBox does not. TypeBox generates independent validation function only when user defines it as independent.
Unfortunately, typescript-json never can follow such performance tuning by accessing object property directly like TypeBox does. Such direct accessment only can be done by TypeBox because TypeBox let users to define data schema and determine whether to utilize direct accessment or not by themselves.
//-------------------------------------------
// GENERATED CODE BY TYPESCRIPT-JSON
//-------------------------------------------
// THIS ONE LINE
const is = TSON.createIs<ObjectSimple>();
// BE CONVERTED LIKE BELOW
const is = (input) => {
const $io0 = (input) =>
"object" === typeof input.scale && null !== input.scale && $io1(input.scale) &&
"object" === typeof input.position && null !== input.position && $io1(input.position) &&
"object" === typeof input.rotate && null !== input.rotate && $io1(input.rotate) &&
"object" === typeof input.pivot && null !== input.pivot && $io1(input.pivot);
const $io1 = (input) =>
"number" === typeof input.x &&
"number" === typeof input.y &&
"number" === typeof input.z;
return "object" === typeof input && null !== input && $io0(input);
};
//-------------------------------------------
// TYPEBOX
//-------------------------------------------
// THIS METHOD CALL
const check = TypeBoxObjectSimple.Code();
// RETURNS BELOW FUNCTION
const check = (value) => {
return (
(typeof value === 'object' && value !== null && !Array.isArray(value)) &&
(typeof value.scale === 'object' && value.scale !== null && !Array.isArray(value.scale)) &&
(typeof value.scale.x === 'number') &&
(typeof value.scale.y === 'number') &&
(typeof value.scale.z === 'number') &&
(typeof value.position === 'object' && value.position !== null && !Array.isArray(value.position)) &&
(typeof value.position.x === 'number') &&
(typeof value.position.y === 'number') &&
(typeof value.position.z === 'number') &&
(typeof value.rotate === 'object' && value.rotate !== null && !Array.isArray(value.rotate)) &&
(typeof value.rotate.x === 'number') &&
(typeof value.rotate.y === 'number') &&
(typeof value.rotate.z === 'number') &&
(typeof value.pivot === 'object' && value.pivot !== null && !Array.isArray(value.pivot)) &&
(typeof value.pivot.x === 'number') &&
(typeof value.pivot.y === 'number') &&
(typeof value.pivot.z === 'number')
);
}
Other benchmarks
// ALLOW SUPERFLUOUS PROPERTIES
export function is<T>(input: T | unknown): input is T; // true or false
export function assertType<T>(input: T | unknown): T; // throws `TypeGuardError`
export function validate<T>(input: T | unknown): IValidation; // detailed reasons
// DO NOT ALLOW SUPERFLUOUS PROPERTIES
export function equals<T>(input: T | unknown): boolean;
export function assertEquals<T>(input: T | unknown): T;
export function validateEquals<T>(input: T | unknown): IValidation;
// DATA STRUCTURES
export interface IValidation {
success: boolean;
errors: IValidation.IError[];
}
export namespace IValidation {
export interface IError {
path: string;
expected: string;
value: any;
}
}
export class TypeGuardError extends Error {
public readonly method: string;
public readonly path: string | undefined;
public readonly expected: string;
public readonly value: any;
}
TypeBox showed amazing performance in the is()
function. However, the is()
function returns only boolean value which means input
value is keeping type T
or not. If you want to more information about the type checking, you should use other functions instead and weakpoint tof TypeBox exists there.
Also, AOT compilation would be done when compiling TypeScript source code. However, JIT compilation would be done in runtime, when running JavaScript program. Therefore, when comparing the 1st validation time, result would be extremely different like below.
Recommend TypeBox
Usage of TypeBox is similar with io-ts and zod, but it is much powerful and faster than them. Also, TypeBox can generate JSON schema very easily. Therefore, if you're looking for a validator library for new project and not suffering from legacy codes, I think TypeBox would be much better choice than io-ts and zod. TypeBox can totally replace them.
Comparing with my library typescript-json, of course, I think that my library is much easier and stronger. As you know, typescript-json does not any extra schema definition. It only requires pure TypeScript type and only one line function call. However, typescript-json needs TypeScript compiler and additional plugin configuration on tsconfig.json
. Therefore, if you're developing on JavaScript environment, typescript-json never can be adopted. In such case, TypeBox would be the best alternative solution.
Next article maybe
React components can be automatically composed by using only TypeScrit type. It also requires only one like as typescript-json does.
Top comments (3)
10,000x... The frontend world just keeps showing me astonishing numbers
We all need this 0.001ms speed boost...
Interesting topic, thanks.