In general, to find the, you know, simplest possible representation that allows types to be composed together, compared against one another, and reduced fully was really such an amazing problem to work on, and it led to a lot of really significant new capabilities within validation as well.
By having this type information, for example, we can do things like discriminate unions automatically. So for example, in archetype, you can just define a union like this directly, and essentially we will, within our type system, identify the optimal discriminant, or set of discriminants, in case, you know, it requires multiple checks to figure out which branch you're on, and we'll implicitly, you know, optimize the actual runtime validation process around leveraging those checks so that most unions are, without you even having to think about it, going to be checked in constant time, whereas other existing validators without that type system information are either just going to be checking it linearly, or they're going to require you to, you know, opt in and manually specify it through a syntax like this.
Zod, I think, actually, specifically recently was having some issues maintaining their discriminated union types, and, you know, even defining it explicitly, it's really expensive at a type level. You can see there's a massive disparity of about 20 times fewer instantiations for the union here, and of course it's just a much more straightforward type to define and read as well. You mouse over it, you see exactly what the union is, versus we go here, and I mean, I don't use Zod constantly, but it's a lot harder to tell what's going on exactly without being able to, you know, see many of the parameters, and maybe you have to pull it out with Zod.infer. But there are some pretty significant disparities, and largely those come out of the two major areas we've discussed. The ability to use the runtime type system to optimize the validation process, so we don't need to rely on user-provided information like this, as well as the optimizations in TypeScript's type system to ensure that this can be parsed much more efficiently.
So, in terms of runtime performance in general, Archetype has been very heavily optimized, and for the sort of base case checking simple props on an object is going to be basically identical to some of the top runtime validators performance wise that are out there, like Tibia and TypeBox. But where it really shines is these cases where it can leverage its deep type system knowledge, for example, to implicitly discriminate a large union. It could very easily result in speeds of 20 or 30 times faster than even those most performant validators in cases where, again, it can, through multiple steps, identify which checks it needs to make sequentially in order to determine which branch of a union it's on, and often we'll be able to check that union in constant time without you having to even think about it. Whereas again, the alternatives are sort of generally to not have that option at all, or to have to build in that logic manually and maintain it as your tech changes. And as a point of reference, those base cases I was talking about for existing performant validators are already about 400 times faster than Zot for these kinds of scenarios. So, certainly, if you need to optimize around performance, there's going to be some really big gains to be had in that area as well.
So I know we're sort of running out of time here, I just wanted to demo a couple of my favorite features from the upcoming release, which hopefully, I guess, is the current release now that you're seeing this video. It's been hard getting to this point, there's so many things that I wanted to cover. The scope definitely grew quite a bit beyond what I had anticipated in terms of the improvements for beta. But hopefully, if you're seeing this, and I was able to wrap everything up the way that I intended, you can try all these things out now. But regardless, I wanted to show off some of these generic inference capabilities, which I feel are very cool, so there's a new feature in beta that you can define these generic types. You can define a signature like this and then reference a type parameter and then instantiate it later on and you just get this one to one inference. Again, it's just like TypeScript. And then, it will work for runtime validation as well. You can define them within a scope, which is essentially a way to bind keywords, your own custom keywords, to whatever types that you want. So, this one is probably the most kind of mind-blowing type within TypeScript's type system that's in here. You can see we've got this alternate generic that takes A and B and it calls itself, and then swaps A and B. This is just a classic TypeScript recursive generic, but this can actually be inferred within archetype and you can see that, in fact, you get these alternating inputs. If we instantiate it later, we pass it off or on instead of 01, you get the expected result and those two are toggling back and forth. So, the fact that all this ends up actually being possible within TypeScript was really incredible for me to discover. Again, props to the TypeScript team for creating such an amazing parser, an amazing tool that would support probably unintentionally this kind of level of depth just within its own type system. I think it's incredible.
Comments