sinclairzx81 / typebox

Json Schema Type Builder with Static Type Resolution for TypeScript
Other
4.65k stars 150 forks source link

Type.Schema #802

Closed pterrien closed 4 months ago

pterrien commented 4 months ago

Hello,

I'm trying to validate data that must contain a TypeBox schema property: in my case, these are plugin definitions, that must provide a schema to validate their config file against. Something we could translate to TS like this:

interface IPlugin {
    config: TSchema
}

I couldn't find anything like a Type.Schema, could you kindly guide me on the best way to implement it? Additionally, would it be possible to restrict the type of schema? In my case the corresponding TS type would be TObject obviously.

At this time, my best bet is to implement a schema that would cope with the TSchema (or TObject) definition defined in your code:

export interface TSchema extends TKind, SchemaOptions {
  [ReadonlyKind]?: string
  [OptionalKind]?: string
  [Hint]?: string
  params: unknown[]
  static: unknown
}

Thanks in advance for your help.

sinclairzx81 commented 4 months ago

@pterrien Hi,

There's a few of ways to approach this, most of which comes down to how you want your plugin API to look, and how types contribute to inference elsewhere. Perhaps the following helps?

TypeScript Link Here

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

// if you want a specialized object type
export interface TPlugin<Config extends TSchema> extends TObject<{ config: Config }> { }

// this is a factory function for the type above
export function Plugin<Config extends TSchema>(config: Config): TPlugin<Config> {
  return Type.Object({ config })
}

// A and B are types of TPlugin
const A = Plugin(Type.Object({ x: Type.Number(), y: Type.Number() }))

const B = Plugin(Type.Object({ a: Type.String(), b: Type.String() }))

type A = Static<typeof A>

type B = Static<typeof B>

Additionally, would it be possible to restrict the type of schema? In my case the corresponding TS type would be TObject obviously.

You can constrain the generic Config parameter to be of TObject only in the following way.

TypeScript Link Here

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

export interface TPlugin<Config extends TObject> extends TObject<{ config: Config }> { }

export function Plugin<Config extends TObject>(config: Config): TPlugin<Config> {
  return Type.Object({ config })
}

const X = Plugin(Type.String()) // error: Argument of type 'TString' is not assignable to parameter of type 'TObject<TProperties>'.

Hope this helps! S

pterrien commented 4 months ago

Once again, thank you for your super quick answer, the level of support on this repo is truly amazing. :) That's a very straightforward and elegant solution indeed.

Many thanks, that really helps!