Sinclair Q&A #252
-
Hi @sinclairzx81! Thanks for opening up discussions! Here are some questions I have been wondering about with respect to typebox for some time:
|
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 5 replies
-
@jessekrubin Hi, see answers below. Do you plan on plan on publishing es module(s)?Yes. I may take another look at ESM publishing relatively soon based on comment #230 (comment). However for the time being, CJS publishing for NPM should work fine across both CJS and Node ESM. For Deno, Do you think you will add support for json type def?JSON Type Definition has been considered, but probably won't make it into TypeBox as standard. There's a couple of reasons for this, but I feel the main one is that JTD doesn't really capture all the semantics of TypeScript's structural type system. JTD does seem largely orientated towards specifying schematics for nominal type systems (which is perfect for languages like C#), but does come at a cost of constraining type composability for TypeScript / JavaScript which is a bit at odds with TypeBox and the expectations of TypeScript users. With this said, see below for a reference implementation of TypeBox JSON Type Definition if you need this. TypeBox JSON Type Definitionimport { Type, Static, TUnsafe } from '@sinclair/typebox'
// ------------------------------------------------------------------------
// TypeDef Namespace
//
// https://jsontypedef.com/docs/jtd-in-5-minutes/
// ------------------------------------------------------------------------
export type StaticTypeDefUnion<D extends string, M extends Record<string, TUnsafe<any>>> = { [K in keyof M]: {[P in D]: K } & Static<M[K]> }[keyof M]
export namespace TypeDef {
export function Boolean() {
return Type.Unsafe<boolean>({ type: 'boolean' })
}
export function String() {
return Type.Unsafe<string>({ type: 'string' })
}
export function TimeStamp() {
return Type.Unsafe<string>({ type: 'timestamp' })
}
export function Float32() {
return Type.Unsafe<number>({ type: 'float32' })
}
export function Float64() {
return Type.Unsafe<number>({ type: 'float64' })
}
export function Int8() {
return Type.Unsafe<number>({ type: 'int8' })
}
export function Uint8() {
return Type.Unsafe<number>({ type: 'uint8' })
}
export function Int16() {
return Type.Unsafe<number>({ type: 'int16' })
}
export function Uint16() {
return Type.Unsafe<number>({ type: 'uint16' })
}
export function Int32() {
return Type.Unsafe<number>({ type: 'int32' })
}
export function Uint32() {
return Type.Unsafe<number>({ type: 'uint32' })
}
export function Enum<T extends string[]>(values: [...T]) {
return Type.Unsafe<T[number]>({ enum: values })
}
export function Elements<T extends TUnsafe<any>>(element: T) {
return Type.Unsafe<Array<Static<T>>>({ elements: element })
}
export function Properties<T extends Record<string, TUnsafe<any>>>(properties: T) {
return Type.Unsafe<{[K in keyof T]: Static<T[K]>}>({ properties })
}
export function Values<V extends TUnsafe<any>>(values: V) {
return Type.Unsafe<Record<string, Static<V>>>({ values })
}
export function Union<D extends string, M extends Record<string, TUnsafe<any>>>(discriminator: D, mapping: M) {
return Type.Unsafe<StaticTypeDefUnion<D, M>>({ discriminator, mapping })
}
}
// ------------------------------------------------------------------------
// PropertiesType
//
// https://jsontypedef.com/docs/jtd-in-5-minutes/#properties-schemas
//
// ------------------------------------------------------------------------
export type PropertiesType = Static<typeof PropertiesType>
export const PropertiesType = TypeDef.Properties({
x: TypeDef.Float32(),
y: TypeDef.Float32(),
z: TypeDef.Float32(),
})
// ------------------------------------------------------------------------
// ValuesType
//
// https://jsontypedef.com/docs/jtd-in-5-minutes/#values-schemas
//
// ------------------------------------------------------------------------
export type ValuesType = Static<typeof ValuesType>
export const ValuesType = TypeDef.Values(TypeDef.Float64())
// ------------------------------------------------------------------------
// EnumType
//
// https://jsontypedef.com/docs/jtd-in-5-minutes/#enum-schemas
//
// ------------------------------------------------------------------------
export type EnumType = Static<typeof EnumType>
export const EnumType = TypeDef.Enum(["FOO", "BAR", "BAZ"])
// ------------------------------------------------------------------------
// ElementsType
//
// https://jsontypedef.com/docs/jtd-in-5-minutes/#elements-schemas
//
// ------------------------------------------------------------------------
export type ElementsType = Static<typeof ElementsType>
export const ElementsType = TypeDef.Elements(PropertiesType)
// ------------------------------------------------------------------------
// UnionType
//
// https://jsontypedef.com/docs/jtd-in-5-minutes/#discriminator-schemas
//
// ------------------------------------------------------------------------
export type UnionType = Static<typeof UnionType>
export const UnionType = TypeDef.Union('eventType', {
"USER_CREATED": TypeDef.Properties({
id: TypeDef.String()
}),
"USER_PAYMENT_PLAN_CHANGED": TypeDef.Properties({
id: TypeDef.String(),
plan: TypeDef.Enum(['FREE', 'PAID'])
}),
"USER_DELETED": TypeDef.Properties({
id: TypeDef.String(),
softDelete: TypeDef.Boolean()
})
}) Why do you use namespaces?I generally prefer namespaces over module level function exports as module level exports have a potential to name collide when exporting them as a set of top level functions to the caller. Also, namespace allows one to potentially transform a namespace into a class instance without breaking users. This aspect can be useful in cases where users may ask to be able to extend a namespace in future. The current Why do you use PascalCase for function names?TypeBox uses pascal casing as this convention is typically used to define types in general. Originally, TypeBox types where named so after the built in JavaScript types String, Number, Boolean, Object and Array where each type mapped to the respective primitive type and adopted the same casing rules. This was loosely imagined as the following if expressed as TypeScript. // TypeScript
interface CustomerCreateRequest { // Pascal
id: Number // Pascal
name: String // Pascal
online: Boolean // Pascal
}
// TypeBox
type CustomerCreateRequest = Static<typeof CustomerCreateRequest>
const CustomerCreateRequest = Type.Object({
id: Type.Number(),
name: Type.String(),
online: Type.Boolean()
}) Generally though, I find it's helpful to draw a distinction in JavaScript between values which are used as data, and values which are to be used as types/schematics. JavaScript has no such distinction (everything is a value), so the pascal naming convention helps make that distinction a little more clear. As for the rest of the library, it simply adopted the pascal casing from Where does code generation stand?The current codegen module was written for a couple of reasons. The first is that as TypeBox moves closer to 1.0, the library ideally needs to be fully mappable to and from TypeScript. In this respect, any valid TypeScript type should map fully to an associated TypeBox type and lose nothing in the process. The codegen module you see is written to help assert this. The other reason is that a few users have asked for codegen tooling. Based on these requests, the codegen module may be used in a future website that allows users to copy and paste TS code and automatically generate TypeBox types (It would be great to have some community help around this). Alternatively, the module may also be used to build CLI tooling external to the library and integrated into tool chains. Outside of this, are no immediate plans to offer CLI tooling or other code generation features as part of the library (as I feel these are best implemented by the community for their particular use cases) What's the easiest way to dump a schema type to a more easily readable form?For JSON Schema const T = Type.Object({
x: Type.Number(),
y: Type.Number(),
z: Type.Number(),
})
console.log(JSON.stringify(T, null, 2)) For TypeScript Possibly https://github.com/sinclairzx81/typebox/blob/master/codegen/typescript.ts Hope that helps! |
Beta Was this translation helpful? Give feedback.
-
@sinclairzx81 What is roadmap for typebox? What does typebox “next” have in store? |
Beta Was this translation helpful? Give feedback.
@jessekrubin Hi, see answers below.
Do you plan on plan on publishing es module(s)?
Yes. I may take another look at ESM publishing relatively soon based on comment #230 (comment). However for the time being, CJS publishing for NPM should work fine across both CJS and Node ESM. For Deno,
esm.sh
is the preference until the newnpm:
import specifiers are fully supported.Do you think you will add support for json type def?
JSON Type Definition has been considered, but probably won't make it into TypeBox as standard. There's a couple of reasons for this, but I feel the main one is that JTD doesn't really capture all the semantics of TypeScript's structural type system. JTD does seem largely o…