I have an Express API server app and a React client app both implemented in TypeScript. I defined my data models using TypeScript interfaces, and I'm using those interfaces on both ends of the system. However, TypeScript interfaces are compile-time feature only, and I need also runtime type checking, e.g. validating that HTTP POST data (json) conforms to the defined data structure.
So my question is, how could/should I implement runtime object validation utilizing the features provided by TypeScript?
I've created a super lightweight library called Smoke Screen which does exactly that. It leverages typescript features to perform any kind of object validation within javascript runtime. It's not 100% seamless due to the fact that javascript does not hold any type information at runtime, but thanks to TypeScript decorators, this may be easily done:
class Person {
@exposed({type: Number})
age: number;
}
// serialize a Person object into a JSON string
const person = new Person();
person.age = 56.8;
const smokeScreen = new SmokeScreen();
smokeScreen.toJSON(person); // -> '{"age":56.8}'
// deserialize a JSON string into a Person object
let json = JSON.stringify({age: 19});
const person2 = smokeScreen.fromJSON(json, Person);
console.log(person2); // -> Person { age: 19 }
// typing validation
json = JSON.stringify({age: "oops"});
smokeScreen.fromJSON(json, Person); // Error: illegal input - property 'age' must be a number
Additional custom validators may be set, optional parameters and null checking are also supported and enforced. I suggest reading more about it and trying it out.
This question is old, but I'd like to share my validation library also.
It's type-script friendly, tiny (no tons of unnecessary functionality) and easily extensible by custom validators.
npm: https://www.npmjs.com/package/checkeasy
github: https://github.com/smbwain/checkeasy
import {alternatives, arrayOf, int, object, oneOf, optional, string} from 'checkeasy';
const myValidator = object({
a: int({max: 5}),
b: string(),
c: optional(float()),
d: oneOf(['a', 'b', 7] as const),
e: alternatives([string(), int()]),
f: arrayOf(string()),
g: object({
subP: string(),
}),
});
const value = myValidator(anyUnsafeData, 'name');
// type of value is: {
// a: number,
// b: string,
// c: number | undefined,
// d: "a" | "b" | 7,
// e: string | number,
// f: string[],
// g: {subP: string},
//}
It also throws clear human readable messages in errors. E.g.
myValidator({a: 'hello'}, 'data');
// throws: [data.a] should be an integer
myValidator({a: 1, b: 'string', d: 'a', e: true}, 'data');
// throws: All alternatives failed for [data.e]:
// [data.e.@alternative(0)] should be a string
// [data.e.@alternative(1)] should be an integer
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With