Skip to content

Migrate from @sinclair/typebox 0.34.x to typebox 1.0#364

Open
masad-frost wants to merge 3 commits intomainfrom
typebox-1.0-migration
Open

Migrate from @sinclair/typebox 0.34.x to typebox 1.0#364
masad-frost wants to merge 3 commits intomainfrom
typebox-1.0-migration

Conversation

@masad-frost
Copy link
Member

@masad-frost masad-frost commented Mar 13, 2026

Why

TypeBox 1 has been out for some time now, we should start using it so that we're not left in the dust!

What changed

Do necessary work per

Versioning

No breaking schema-level or wire-format changes introduced at all, validated through a new tests

  • Breaking protocol change
  • Breaking ts/js API change
    Consumers will have to now use typebox >=1.0.0 and use the exported UInt8ArrayType and DateType

@masad-frost masad-frost requested a review from a team as a code owner March 13, 2026 02:16
@masad-frost masad-frost requested review from wernst and removed request for a team March 13, 2026 02:16
Copy link
Contributor

@Monkatraz Monkatraz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks quite reasonable, surprised we didn't have to do more honestly

payload: {
code: 'SOME_ERROR',
message: 'something went wrong',
extras: { detail: 'extra info' },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did anyone accidentally shove entire error class instances in these things anywhere? wonder what 1. typebox does with that and 2. if it did "handle" it before, if it still does.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extras is just a generic Record so typebox didn't do anything with them, the codec probably resulted in Error objects being {} since the properties are usually non-enumerable.

});
});

describe('composite types', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we do use some Type.Intersect and other more esoteric types, might be worth checking those.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll throw them in

@@ -1,4 +1,4 @@
import { TNever, TObject, Type } from '@sinclair/typebox';
import { TNever, TObject, Type } from 'typebox';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh wow, he actually got the typebox package name?

...options,
}),
(value): value is Uint8Array => {
if (!(value instanceof Uint8Array)) return false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sanity check: no insane bullshit with node Buffer right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines +26 to +35
if (
typeof options.minByteLength === 'number' &&
value.byteLength < options.minByteLength
)
return false;
if (
typeof options.maxByteLength === 'number' &&
value.byteLength > options.maxByteLength
)
return false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use some curlies bro

Comment on lines +66 to +75
if (
typeof options.minimumTimestamp === 'number' &&
value.getTime() < options.minimumTimestamp
)
return false;
if (
typeof options.maximumTimestamp === 'number' &&
value.getTime() > options.maximumTimestamp
)
return false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lol, I'll throw in some lint rules

Comment on lines +351 to 359
const RecursivePayload = Type.Cyclic(
{
RecursivePayload: Type.Object({
n: Type.Number(),
next: Type.Optional(Type.Ref('RecursivePayload')),
}),
},
'RecursivePayload',
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it bothers me that he made the defs go first

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lol

Comment on lines +355 to +360
// In TypeBox 1.0, TSchema is {} so Static<TSchema> doesn't resolve the same way
// as in 0.34. Using `any` for schema type params in the implementation signature
// is safe because the public overload signatures still enforce correct types for
// all callers. The implementation body doesn't inspect handler types at runtime.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
handler: RpcProcedure<any, any, any, any, any, any>['handler'];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah this makes sense, it's annoying how you can't use describe unknown extends ... or infer or something.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah was painful for me and my agent and it went in circles, until I realized who cares lol.

Comment on lines +144 to +146
* Flattens a union-nested error schema into a single level in order to
* satisfy ProcedureErrorSchemaType which accepts only a single level of union
* so that we can enforce a schema validation on the error schema.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah the original comment was quite spiteful wasn't it

type TLiteralString = TLiteral<string>;

type TEnumString = TEnum<Record<string, string>>;
type TEnumString = TEnum<Array<string>>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

huh. enums are no longer TypeScript shaped, I guess? that might be a really painful migration...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, I think it's for the better, they're JSON Schema shaped.

Copy link
Contributor

@Monkatraz Monkatraz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cache change looks fine

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants