sinclairzx81 / typebox

Json Schema Type Builder with Static Type Resolution for TypeScript
Other
4.56k stars 148 forks source link

How to check if a schema conforms? #877

Closed Qrokqt closed 1 month ago

Qrokqt commented 1 month ago

I'm looking for a way to pass a schema to a function and have typescript throw an error if the schema doesnt conform to a type, is this possible?

// not sure how to represent this in TypeBox
interface ValidSchema {
  [key: string]: string;
}

function doSomething<T extends ValidSchema>(arg1: T) {
}

//good
doSomething(Type.Object());
doSomething(Type.Object({
  foo: Type.String(),
  bar: Type.String(),
}));

//bad
doSomething(Type.Number());
doSomething(Type.Object({
  foo: Type.Number(),
});
doSomething(Type.Object({
  foo: Type.Object({
    bar: Type.String(),
  }),
}));
sinclairzx81 commented 1 month ago

@Qrokqt Hi, try the following...

TypeScript Link Here

import { Type, TObject, TString } from '@sinclair/typebox'

type Allowed = TObject<{ [key: string]: TString }>

function doSomething<T extends Allowed>(schema: T) { /* ... */ }

//good
doSomething(Type.Object({}));
doSomething(Type.Object({
  foo: Type.String(),
  bar: Type.String(),
}));

//bad
doSomething(Type.Number());
doSomething(Type.Object({
  foo: Type.Number(),
});
doSomething(Type.Object({
  foo: Type.Object({
    bar: Type.String(),
  }),
}));

Hope this helps S

Qrokqt commented 1 month ago

Perfect, thanks!

photz commented 1 week ago

Unfortunately this is not a general solution. I'm trying to define a SchemaFor<T> but even the simplest attempt runs into a "type instantiation is excessively deep and possibly infinite" error.

This is what I tried:

type SchemaFor<T> = T extends string ? ReturnType<typeof Type.String>
  : T extends number ? ReturnType<typeof Type.Number>
  : T extends boolean ? ReturnType<typeof Type.Boolean>
  : T extends object ? TObject<{ [P in keyof T]: SchemaFor<T[P]> }>
  : TSchema;