Version 1.0.28#1384
Conversation
|
Will/can the result of As long as the result of |
|
@colinhacks Hello, The only aspect of Standard Schema I was interested in exploring was a feature called "Remote Schema Embedding" which would have let Standard Schema be embedded into Json Schema schematics via const T = Compile({
type: 'object',
required: ['zod', 'valibot'],
properties: {
zod: z.number(),
valibot: v.number(),
}
})... and also possibly support for this. const T = z.object({
x: z.number(),
y: z.number(),
z: z.number()
})
// Mapped Zod Types.
const S = Type.Script({ T }, `{
[K in keyof T]: T[K] | null
}`) // const S: TObject<{
// x: TUnion<[TNumber, TNull]>,
// y: TUnion<[TNumber, TNull]>,
// z: TUnion<[TNumber, TNull]>
// }>
// Inference Zod Types.
const X = Type.Script({ T }, `T extends {
x: infer X,
y: infer Y,
z: infer Z
} ? [X, Y, Z] : never`) // const X: TTuple<[
// TNumber,
// TNumber,
// TNumber
// ]>Unfortunately, information regarding the design of Standard Json Schema was not particularly forth coming at the time. And without indication the specification was going to support a minimum set of features (runtime and type-level schema introspection being the main ones), I wasn't prepared to risk Standard Schema being so deeply integrated into TB in the way it was ... so I pulled support.
The Compile and StandardSchema functions are both Adapters (by this definition) const C = Compile({ type: 'string' }) // C: Validator<...>
// │ │ │
// │ │ └────────> Json Schema
// │ │
// │ └──────────────────> Adapter
// │
// └──────────────────────> Validator
const S = StandardSchema({ type: 'string' }) // S: StandardSchemaV1<...>
// │ │ │
// │ │ └───> Json Schema
// │ │
// │ └──────────────────> Adapter
// │
// └──────────────────────> StandardSchema One function is more descriptive, doesn't require a "Validator" to be considered a "Schema" and separates concerns related to runtime Result and Issue mapping (as mandated by this specification). Also Effect
For the purposes of system integration, we have the same goals ... no adapters
The problem is that Standard Schema encodes Zod-like design patterns (schema / validator coupling) while presenting this design as the "Standard". This will negatively impact users of alternative software design patterns (including users of the Json Schema specification) which now need adapters to be considered "Standard" with diminished DX. The broader concern is that Standard Schema adoption will lockout alternative software design when that doesn't need to be the case. Should Json Schema see standardization under Ecma International, developers wanting to target the specification will need adapters through Standard Schema to use it (as is the case for TB) Adapters do provide clear explicit transformation (they are fine, fwiw) const A = z.toJSONSchema(z.string()) // Standard Schema > Json Schema
const B = StandardSchema(A) // Json Schema > Standard Schema Standard Json Schema looks like it is trying to turn this server.post('/', { // 2. server compiles json schema
body: { type: 'string' } // 1. caller passes json schema
}, (...) => {...})into this ... server.post('/', {
body: { type: 'string' } // error: "Json Schema" is not "Standard Json Schema"
}, (...) => {...}) // (i.e. use Zod, Valibot, ArkType)What should I make of this? I am open to continued discussion, but my preference is to have TypeBox removed from the "Implementer" list as I do not provide direct support for this specification. I only provide a reference / example adapter to it (which is not published with the library) https://github.com/sinclairzx81/typebox/tree/main/example/standard I will take a look at the spec again next year once Json Schema has been added. |
This PR drops support for Standard Schema and reverts to providing an implementation via an example adapter.
Rationale
TypeBox is a JSON Schema builder and validation compiler. Standard Schema is a set of TypeScript interfaces for Zod-likes.
Internal support for Standard Schema was attempted but proven to complicate TypeBox’s internal composition, inference and validation mechanisms when trying to interpret Standard Schema as a Schema.
Continued support for "Standard Schema Embedding" was deemed unlikely to sit well with longer term goals related to JSON Schema compliance, and where adoption was leading to excessive complexity for little to no benefit to TypeBox. In addition, the specification was observed to cause complications for framework integrators (particularly OpenAPI frameworks that require Json Schema support) and in many cases, specification adoption put TypeBox at odds with the infrastructure it was designed for.
Primary Considerations
TypeScript interfaces are neither standards nor schematics, and the terminology used by Standard Schema is at odds with its purpose. A more accurate name for the spec would have been "Standard Validator". One can only speculate at the reasons why one would decide to call a TypeScript validation interface a "Schema".
A compiled TypeBox type is not a schema; it is a compiled validator. Implementing Standard Schema on a compiled validator would force TypeBox to blur the distinction between schema and validator from a conceptual standpoint.
Standard Schema is designed for Zod-likes that couple schematics to validation. TypeBox is decoupled by design and intended for compatibility with Json Schema compliant validators. TypeBox's design renders Standard Schema fundamentally incompatible because it would force TypeBox to couple schematics to validation.
The Standard Schema specification seeks improved DX for Zod-likes by hiding explicit compile or binding calls (e.g. adapters) to framework interfaces, but hinders integration for everything else. Libraries that make the principled decision to decouple schematics from validation (such as TypeBox) are made to suffer diminished DX even in situations where the library would otherwise be natively supported.
Irrespective of the diminished DX involved with wrapping TB types (via Compile), the general observation has been that naive adoption of Standard Schema (on the principle it is a Schema) is forcing implementation patterns that run contrary to best practice software design and can (in some cases) lead to undue complexity for the hosting framework (notable for JSON Schema dependent systems)
Historical
TypeBox is written to promote sound system design by encouraging infrastructure to embrace cross-language, interoperable schematics for data representation. The TypeBox view (my view) is that libraries should emit JSON Schema (or other specification) so that trusted, secure, and high-performance validators (such as Ajv) can enforce data validation constraints consistently across languages and network boundaries.
Attempts to Integrate Standard Schema
In TBV1, attempts were made to try and integrate Standard Schema as a sub schema of JSON Schema. This would have enabled Zod-likes to be embedded into JSON Schema via keyword extension, and where TypeBox could assume a role of validation and inference system for both JSON Schema and Standard Schema specifications (pre-1.0.28 versions have this capability). The intent was to provide assistance for frameworks having difficulties integrating both Json Schema and Zod-likes.
Unifying Json Schema with Standard Schema was a significant consideration during TBV1 development.
However, given the complexities of hosting remote libraries in JSON Schema, and since many Standard Schema implementations now support JSON Schema translation, I see little benefit in trying to pursue remote library compatibility via Standard Schema when these libraries can be made to interoperate by transforming them to JSON Schema.
Support for JSON Schema translation in remote libraries was the main reason for dropping Standard Schema, along with additional concerns that the design of Standard Schema (and subsequent adoption) was rendering JSON Schema (and by extension TypeBox) incompatible with OpenAPI in various ecosystem projects (as per reference link).
Final Thoughts
TypeBox considers JSON Schema to be the only (current) viable canonical representation for runtime types, one that enables TypeScript to be encoded as a network-transmissible schema format that can be understood by any system or language with a compliant validator. If efforts had been put into establishing JSON Schema (or an extension thereof) as a common representational format shared by all libraries, there wouldn't be a need for Standard Schema at all.
Related: https://json-schema.org/draft/2020-12/json-schema-core#name-non-json-instances
The move to JSON Schema appears to be happening gradually, as most libraries are offering some form of JSON Schema translation support in light of AI / MCP tooling requirements. This raises questions about the necessity of Standard Schema, particularly when it has been demonstrated for years that both validation and TS inference can be derived entirely from JSON schematics.
Ultimately, my view is that runtime type libraries should be nothing more than compositional builders to assist with the construction of formal schematics and nothing more. Data validation should be delegated to trusted, secure, high performance systems that are vetted to take on the critical role of ensuring data integrity. This has been the TypeBox modus operandi since the very beginning.
References:
Specification: JSON Schema Specification
Ecma: JSON Schema Standardization under Ecma International.
Implementations: Valibot | Zod | ArkType.
This update is being pushed under 1.0.x stabilization revisions. Apologies to early adopters.
Fallback
The following adapter is located in
example/standard/standard.ts