TypeScript Interface Runtime Validator
ts-interface-checker is a runtime validation library for TypeScript interfaces, currently stable at version 1.0.2. It works in conjunction with `ts-interface-builder` (a build-time tool) to generate JavaScript modules that encapsulate the structural checks derived from TypeScript interface definitions. This approach allows developers to validate arbitrary JavaScript objects (e.g., parsed JSON from network requests or configuration files) against their TypeScript types at runtime, providing detailed error messages upon failure. The library itself is lightweight and focuses solely on the runtime checking mechanism, making it suitable for environments where type safety beyond compile-time is desired, such as API boundary validation. It supports checking properties, optional fields, and even method call arguments and return values as defined in interfaces. It maintains a stable release cadence for its core functionality.
Common errors
-
Error: value.size is missing
cause An object being checked against an interface is missing a required property.fixEnsure the object being validated includes all non-optional properties defined in the TypeScript interface. For example, `Square.check({ size: 1 });` instead of `Square.check({ color: 'green' });` for an interface requiring `size`. -
Error: value.color is not a string
cause A property in the object being checked has an incorrect type according to the interface definition.fixVerify that all properties in the validated object conform to their specified TypeScript types. For example, `Square.check({ size: 4, color: 'blue' });` instead of `Square.check({ size: 4, color: 5 });`. -
Error: value.name is not a string
cause When validating method arguments, a parameter has an incorrect type.fixEnsure arguments passed to `methodArgs().check()` match the types defined for the method's parameters in the interface. For example, `Greeter.methodArgs("greet").check(["Bob"]);` instead of `Greeter.methodArgs("greet").check([17]);`. -
Error: value.name is missing
cause When validating method arguments, a required parameter is missing from the argument list.fixProvide all required arguments when calling `methodArgs().check()`. For example, `Greeter.methodArgs("greet").check(["Alice"]);` instead of `Greeter.methodArgs("greet").check([]);`. -
Error: value is not a string
cause When validating a method's return value, the value does not match the specified return type.fixEnsure the return value passed to `methodResult().check()` matches the method's return type. For example, `Greeter.methodResult("greet").check("hello");` instead of `Greeter.methodResult("greet").check(null);`.
Warnings
- gotcha The `ts-interface-checker` package is a runtime dependency, but it requires definitions generated by `ts-interface-builder` at build-time. Users must install both, with `ts-interface-builder` as a dev dependency.
- gotcha Using `strictCheck()` can lead to backward compatibility issues. If new optional properties are added to an interface, older code with `strictCheck()` will fail when validating data containing these new properties.
- gotcha The library does not currently support TypeScript generics (except for Promises, which are unwrapped). Interfaces with complex generic types will not be correctly processed by `ts-interface-builder`.
Install
-
npm install ts-interface-checker -
yarn add ts-interface-checker -
pnpm add ts-interface-checker
Imports
- createCheckers
const { createCheckers } = require('ts-interface-checker');import { createCheckers } from "ts-interface-checker"; - Checker
import type { Checker } from "ts-interface-checker"; - Generated Type Suites (e.g., fooTI)
import * as fooTI from "./foo-ti";
import fooTI from "./foo-ti";
Quickstart
// 1. Define your TypeScript interface (e.g., in `foo.ts`)
// interface Square {
// size: number;
// color?: string;
// }
// 2. Generate runtime checker code using ts-interface-builder (build step)
// npx ts-interface-builder foo.ts
// This creates `foo-ti.js` or `foo-ti.ts`.
// The content of `foo-ti.js` would look something like:
/*
// foo-ti.js
import * as t from "ts-interface-checker";
export const Square = t.iface([], {
"size": "number",
"color": t.opt("string"),
});
const exportedTypeSuite = { Square };
export default exportedTypeSuite;
*/
// 3. At runtime, import the generated type suite and create checkers.
// For a self-contained example, we'll mock the `foo-ti` module content directly:
// A minimal mock of what `foo-ti.js`'s default export would look like to `createCheckers`
// In a real application, you would `import fooTI from './foo-ti';`
const mockFooTI = {
Square: { // This structure would be generated by ts-interface-builder
name: "Square",
check: (value: any) => {
if (typeof value !== 'object' || value === null) throw new Error('value is not an object');
if (typeof value.size !== 'number') throw new Error('value.size is not a number');
if ('color' in value && typeof value.color !== 'string') throw new Error('value.color is not a string');
}
}
};
// Import createCheckers from the library
import { createCheckers } from "ts-interface-checker";
const { Square } = createCheckers(mockFooTI); // Pass in the generated type suite
console.log("Checking valid objects:");
Square.check({ size: 1 }); // OK
Square.check({ size: 1, color: "green" }); // OK
console.log("Valid objects checked successfully.");
try {
console.log("\nChecking invalid object (missing property):");
Square.check({ color: "green" });
} catch (e: any) {
console.error(`Error: ${e.message}`); // Expected: Error: value.size is not a number
}
try {
console.log("\nChecking invalid object (wrong property type):");
Square.check({ size: 4, color: 5 });
} catch (e: any) {
console.error(`Error: ${e.message}`); // Expected: Error: value.color is not a string
}