Video details

TypeScript Berlin Online Meetup #4 + Quiz with prizes!

TypeScript
10.22.2020
English

🙌 During this Meetup you can take part in a quiz! Answer the questions prepared by our speakers and win exciting prizes - a lifetime Quirrel Pro membership (https://quirrel.dev​) and Prisma swag (a tshirt, a mask and stickers). 🎁
00:00​ : Welcome & Announcements by Daniel Norman 02:30​ : 🗣Ethan Arrowood - Advanced TypeScript Definition Patterns with Fastify 36:45​ : Quiz Interval 40:33​ : 🗣Phoomparin Mano -
You might not need advanced types 1:11:53​ : Quiz Interval 1:16:43​ : 🗣Simon Knott - Fully-typed fullstack development using Blitz.js 1:57:06​ : Quiz Finale 2:00:00​ : New Meetup Group Announcement: Advanced TypeScript Trickery

Transcript

- Welcome. We are live, welcome to TypeScript Berlin Meetup number four. So while this event started in Berlin, the online format has allowed us to bring together the TypeScript community from across the globe. So I'm your host, Daniel. And today we have three speakers who are joining us with three exciting talks. The speakers are joining us from three continents, and in the order of the talks, we have Ethan joining us from Massachusetts in the U.S., Poom joining us from Bangkok, Thailand, and Simon, joining us from Potsdam in Germany. So welcome everyone. And before we get started, I have a couple of announcements to make. So today, we have a prize to give away and our prize for today includes a Prisma face mask, a Prisma t-shirt, some Prisma stickers, and last but not least, we have a lifetime pro membership to Quirrel. In case you're not familiar with Quirrel, Quirrel is a task queuing solution for serverless deployments created by Simon Knott, who will also be speaking today. He's also one of the core contributors to Blitz.js. So you can check out Quirrel at quirrel.dev. Simon, you can probably drop the link to that in the chat. And then finally, I'd like to mention that this event is organized by Prisma, and Prisma in case you're not familiar, is the easiest way to work with databases in Node.js and TypeScript. So the way that the quiz is going to work is that we are gonna have the questions after every talk, where you'll be able to join via a QR code or via the link that we'll also drop in the live chat. And so now, without any further delay, I'd like to invite our very first speaker, and that is Ethan Arrowood. So let me just bring Ethan onto the stage. - Hello. - Hey, welcome, Ethan. - Thanks for having me. - Let me get the names here. Is that correct? Yes. So Ethan, you are a maintainer for Fastify and an active source contributor to Node.js. You're currently employed as a software engineer by Microsoft based out of Cambridge in Massachusetts. And when you're not working, you can most likely be found hiking, skiing, or camping somewhere in the Northeast U.S. Have you been able to do that this year? - Yes, in fact I actually am living in Vermont right now due to the pandemic, I've escaped the city of Boston and have fled to the woods. - Oh, okay, that must be lovely. - Yes. - Okay, so today you're gonna be talking about advanced TypeScript definition patents with Fastify. - Exactly, yeah, my talk today is gonna be a fun one. We're gonna dive into some Fastify stuff, and also doing some code sharing on TypeScript playground. So if we're ready for it, I can share my screen and kick this off. - Yeah, let's do that. - All right. Let's see here, we're just gonna share the whole screen, that'll be easiest. Cool. - All right. - And you should be able to see this now. All good? Oh, here we go. All right, hello, everyone. Again, my name is Ethan and today, we're just gonna be talking about some Fastify and TypeScript. For those of you who don't know, Fastify is a Node.js web framework, very similar to that of the likes of Express or Hapi. In fact, we work alongside many maintainers from other frameworks inside of Node.js to make sure that we're all working towards a common goal of providing developers the best web framework developer experience as possible. And that's really the core to my talk today, is developer experience. Fastify itself is written entirely in JavaScript, so because of the attraction of TypeScript over the past couple of years, we recognize that Fastify should do something to help those TypeScript developers. And what that was is we went and we wrote our own type definitions, and a lot of projects do do this, or have community contributions through definitively typed, but Fastify was a bit more unique because of the way our application is built, the way Fastify API is built, we had to do a lot more heavy lifting, so to speak. And so without further ado, let me jump into the first topic of my talk today, and that is discriminant unions and function overloads. To define what a discriminant is, it's a characteristic that enables things to be distinguished from one another. And inside of TypeScript, these discriminants can be used for type narrowing. So you've commonly seen things like a switch statement and each case of a switch statement would correspond to some of these distinct characteristics. To give a little bit easier of an example, let's consider this fictitious union I have here, called fruits, and it's made up of an apple, an orange, a strawberry and a watermelon. The common fact for all four of these and what makes them the ability to be like unioned is the fact that they're all fruits, they all have that in common. Similarly, they all have a color and they all have seeds. But the apple and the strawberry are special because they are the color red, the apple and the orange and the watermelon are unique because they all have inner seeds, and the strawberry is unique because it is the only one of this union that has outer seeds. And then of course the orange and the watermelon are also unique in their own ways because of their color. The orange is a well orange and the watermelon on the outside is green. And so this kind of union, while fairly trivial and fairly obvious, there is a distinct difference between each of these four entities, where if I was to ask you, which one of these items is the color red and has outer seeds, you could immediately tell me which one that is. There's no if, ands or buts about it. Similarly, if I asked you what about which one of these items is the color orange? Again, there's only one option. And it's these kinds of characteristics, these unique identifiers, which are considered the discriminants. Now there's other properties that could exist here. For example, I said that these are fruits, they have color, they have different seed orientations. So what if we threw in something like a banana? Now we've added a new color, which would be yellow. In this current example of these four fruits, there is no yellow option here, but the yellow characteristic could still be true in the sense of if we were to consider an expanded union of this sense, we would have to consider the other possibilities. But in the context of Fastify, we use discriminant unions to differentiate between HTTP servers. So let's jump into the playground link and I actually have it already opened for us, and let's make this a little bigger. So we'll start off here where here's our union of four different HTTP servers, the four that are provided from Node.js. The basic HTTP, the HTTPS and then the two different HTTP2 servers. For what it's worth, you would rarely use this one in production because it's by implicitly, if you're using HTTP2, you should use it in a secure mode, but it's important that we have all four available because that is what's offered by Node.js. In our type definitions, let's start with this first interface called the Fastify instance. And this'll make a little bit more sense in a moment. It has a single generic, server extends servers. So this generic parameter server is going to be one of these four. And in this simplified example, we're just immediately passing that generic to this other property raw. In the real Fastify, there's a ton more properties. In fact, in later examples, I'll show you a couple more things that Fastify instance can do than just share what kind of HTTP server it is. But the important thing here is that the Fastify instance needs to have a specific HTTP server. And so how did we do that when we have only one entry point? Well, we use discriminant unions over the different options for this function. So the Fastify function is the one and only entry point to the Fastify API. You can see here an overload, and each one of these overloads has a generic server, which extends from one of the four different HTTP servers. It's also defaulted to that same HTTP server as well. Then on the right, you can see in this same type definition that they all share one parameter, options. And these options though are all different. It starts here with HTTP, then we have HTTPS two and then two Secure. But at the end, all four loads return the same thing, a Fastify instance with that server generic. So what makes this unique? Well, if we take a peek at these type definitions here, we can see where with the HTTP options. This is just a basic type with one optional property logger. And very similarly enable or disable a logger for your Fastify application. Then type is the HTTPS options. It's an intersection of the HTTP1 from above, but it now has a new required property, HTTPS, which corresponds to the server options from the HTTPS library in Node.js. The next type definition is HTTP2. Consider this one, the insecure HTTP2 types, where again, it's an intersection of that first baseline HTTP options object. With this new property that's also required, and it's a Boolean literal for the HTTP2 property. This is how in Fastify, we enable our users to create an HTTP2 server. And then finally, we have the secure option where again, it's that same base object, but the intersection this time has two required properties. HTTP2 is again the Boolean literal of true, and now we have that HTTPS property again, but this time passing the HTTP2 version of the options. So with these four types here and combined with this function overload, TypeScript is smart enough to actually infer which of the four overloads we're using based solely on whatever options object you passed your Fastify instance. What's even more cool is that this baseline definition here, notice how the options are optional. So we can also state that if you don't provide us any options, which is a valid case in Fastify, we will assume that you just want an HTTP server, which is exactly that. Because in consideration, if you want to make an HTTPS or an HTTP2 server, it's an opt in operation. You need to provide us with the certificates and keys to make your server secure. And for HTTP2, we need to know that you wanna switch to that protocol, and that's why we ask you to say, hey, let us know, are you gonna wanna use HTTP2 or not? So to see that, an example, we have these four constants, F one, two, three, and four, and you can see how in this current one there's no options object. And the type sense here says, hey, that's a Fastify instance with a generic of HTTP.Server. Perfect, exactly what we wanted. Same with F2, we're passing in just the HTTPS option, we're not enabling HTTP2 yet. So Fastify knows, hey, we want an HTTPS server, and so on and so forth for the other two. And we can even further exemplify this, or if you do const F1B and we say equals Fastify, and we provide that logger type here, you can see that F1B also gets that HTTP server type, because again, we're not providing HTTPS and we're not providing HTTP2. But if we were to go ahead and add that logger prop to another one of these, it would still work fine. And it still thinks it's the current server, because it has that discriminant characteristics that we created in our function overload. Awesome. So now let's jump back to the presentation and move on to the next fun part of some of the things that we've done in the TypeScript definitions. This one is called Named Generic Parameters, and most JavaScript developers should be very familiar with just named parameters in general, where if you have some function F, it takes a single argument that is some object, and then that object is structured inside of the actual function implementation so that the user can provide the parameters for this function using named attributes, rather than using some sort of argument order. And I can go to the TypeScript playground again, we can do a very similar thing in TypeScript as well. So here in a new TypeScript playground, I'm gonna jump down to the bottom here and talk a little bit more about what we're gonna be walking you through. And this is also gonna be brought up later in the talk as well about this whole request object. So I recommend paying attention to this part just a little bit more. Here is an example of a Fastify server. Well, I'm using a little bit of typecasting, just to protect we're instantiating a real Fastify instance. But the point is there is these methods to create routes on Fastify, just like Express and like Hapi, where the first argument's a string, and then the second argument is this route handler. And this route handler takes this first argument, the request object, and that request object contains the details of the incoming request. Things such as the query string, the body, if it was a poster or put, the headers for basically any request, and then there's also parameters, if that was passed into the URL. And in Fastify, we automatically figure all of that out for you, and we'll get to the reason why we do that for you in a little bit. But just know that it's done for you underneath the hood. So in a JavaScript application, de-structuring these four things from the request object is valid and easy to do. But those that know about runtime issues in TypeScript will immediately be thinking, okay, but like, how can we be sure that this body is of some type? Because it's coming from a external request. And the answer is we don't, we actually need to validate that and we'll get to validation a little bit, but to jump ahead, we wanna say, okay, if we can validate what these four things are from our request object, we should also be able to provide a type for them. And we could use typecasting like this, but typecasting can get a little messy because usually, it's again the developer has to do it. And honestly, most of the times they have to do something like this, where they're like const, and then it's like querystring2 equals querystring as, and then some type, and that's just dependent on the string here. And now, the querystring2 property is this querystring, but as some other type. But we've come up with a better way to do that. And the second example you can see it's the same post requests as I defined here, except that now has a generic. And in this generic, we have corresponding properties for the various aspects of our request object. And then inside of this route handler, you can see that the string type is assignable to the querystring, and same with this record type that we passed here, is assignable to this body. And now the way we're able to achieve that is through these named generic parameters and this whole section's about. So starting up here, we have this interface is called route generic. All four of these properties are optional and they're defaulted to unknown. Then we have this other interface. This interface is the request interface. That is what is corresponding to that request argument in the route handler function. You can see those types here in here, but you can see inside of this interface, we have that generic R, it extends from this route generic interface. And then inside of that interface, we're actually able to use these strings to access this interface, generic interface R. In doing so, it'll default to this unknown value because here online 17, we're saying, hey, R extends that same route generic, but we're also defaulting it to itself, the route generic, and then that's passed down, the route handler, gets that generic parameter R, which it then passes down to the Fastify request. And then at the end, Fastify request gets this generic and can apply it to these four corresponding properties. So this is one of the ways that users can also optionally set different parameters here, where for example, if they still want their headers and parameters to remain as unknown, they can do so just by omitting it. The alternative solution here is having an order generic, which is what most people are used to seeing, where the first one could be querystring, the second body, third headers, fourth parameters. And if you have a request that only wants to specify the parameters type, you would need to then provide either some sort of default for the first three or some sort of undefined or unknown for the first three. And that can get a little messy in your code. So it's much better that we let our users opt in to the properties they want to be typed as they go. And now some might think that this actually is not type safe, and you're right, it's not. It's not inherently type safe right now because there's nothing verifying that this querystring and body here are actually what I'm saying they are, because who knows? What if the user didn't pass in a querystring to their request for this route? Now this querystring might be known, but our type say it's string. So what do we do about that? Well, I encourage you to hold on to that question 'cause I'll answer it in a little bit when we start talking about some of the advantages that come with TypeScript 4.1. So back to the presentation, I have one more little thing I wanna share, and then we'll go back to the ideas of this validation part of our, this dynamic aspect of our data. And this is declaration merging. This is something a lot of people are familiar with, especially those dealing with external modules. Plugin systems are hard in TypeScript, and in Fastify, everything is a plugin. I don't wanna get too much into details what that means, but the point is in this little code snippet, you can see here, we're using one of my favorite plugins Fastify multipart, it enables the multipart kind of request stuff. So you can send files to a Fastify API. And you can see here that this request object now gets this new file method, which is a promise, and it resolves some data object. And that data object contains a readstream under the file property and as well as a writestream, sorry, and then as well as has a file name as a string that can be used in a method such as a writestream for creating a writestream. In this sort of crude example, we're just sort of pumping the data that's coming in straight out into whatever the server host is. So I also have a TypeScript playground link for this, but I don't think it's as valuable because this one is really demonstrated over what would be considered a project, multiple files. And so let's start with the top left here, and this is Fastify.ts. You can see we have two interfaces, request and response. The route handler and route method, which I've already shown previously have been much more simplified for this example, where there is no more generics and we just have the request and the response object, and this route handler is promise based. And then that route wraps that handler as that second argument. And then here inside of our Fastify instance, we have those our HTTP methods get post put, and they all correspond to a route. Then taking a peek at our app.ts file, you can see that we're creating that server based off of what's exported from Fastify. But then inside of our request handler here, notice how we're calling request that file, but this request interface doesn't actually have that property. We have no properties in it for this example. So then how does our project know that the file method exists as well as the file property, as well as this file name property? And that's the power of declaration merging. Inside a FaceTime multipart, which you can see we're importing on line two of our app is declaring this Fastify module. And we're saying inside of that Fastify module, we're going to export this type called data. And data is gonna be a file as a ReadStream and a file name which is a string. And the interface Fastify request now has a new method file that returns a promise that resolves with that data type. And you might be thinking, oh, wait, that's the same name, and that's how declaration merging works. Where if you declare a module, that also exists in your project, such as Fastify.ts here. TypeScript will infer that, okay, let me take all of the types defined inside of this block and merge it with all of the definitions defined in this block. And then inside of my project here at that TS, I get both types. I get those that are defined from Fastify and those that are defined from the plugin Fastify multipart. And yes, this isn't a perfect solution because who's to say just because you're importing Fastify multipart, that you're actually using it? And unfortunately, I don't have a great answer for you. Plugin systems in TypeScript are really hard. And because Fastify written in JavaScript, we're limited to what we can do. If this was a TypeScript project, there might be a better API that we could develop that creates the correct encapsulation for these plugins and such. However, again, this is a JavaScript project and we're providing types of definitions on top of it. So we have to give and get, and we have to meet in the middle or compromise so to speak. So great. Onto the last kind of part of this talk, and that is some of the new template literal type features that are coming with 4.1 and something that I'm really excited about. So here are two tweets that I've had tracked for awhile, the first one on the left is about URL parameters. In fact, it's talking about Express in this example where using these new template literals, you can give some sort of route and then derive the information of that route, just purely using type definitions. And I thought that was really cool and actually really impressive. And now, I don't know how applicable this would be in Fastify since we apply that information through our own API, but it just shows other cool things like template literally type. On the right though, I wanna start getting everyone thinking about a JSON parser using TypeScripts type system. So this example is way too big to fit in a single slide. But if you're interested, you can go to this link here, it's a shortened TypeScript playground, and it has, it's basically this link that this tweet has, but shortened. And I want you to think about, what could the possibilities be if we could build an entire JSON parser using TypeScript definitions alone? Fastify has this concept that I mentioned before about validation and serialization. It utilizes JSON Schema and Jason Schema validation, we are particular library of choices, AJV, another JSON validator, and we use this validation and serialization feature to make our routes faster and more secure. And what I mean by that is here in this example, we're providing this schema as the second argument to this route. When a schema is provided to a route in Fastify, the Fastify engine will take that schema and do two things with it. First, it'll serialize all incoming and outgoing data based on that schema. So if your schema fully covers what your route will do, Fastify is actually able to process incoming routes faster because it knows exactly what data it's gonna be receiving, it doesn't have to do any sort of guessing or checking during the runtime, so to speak. The other thing is the security where if you are defining that your body should be of some shape, it should have some explicit properties and those properties are required and the body doesn't have those properties, then Fastify will actually never even run this handler. We'll just say, nope, 400 or 500 error, this isn't valid. We provide other mechanisms like hooks to, so you could run some code if that error hits. But for the sake of simplicity, just think that if the schema doesn't pass, the route handler doesn't execute. But what TypeScript? Remember before, I showed you how we can provide types for these four properties for our incoming request object. And now, we're validating that request object based on some schema. So how do we connect those two things together? The types of these four properties, as well as the schemas that we're writing for our routes? There are two existing solutions both I've used and really enjoy using. The first one on the left is really great for projects that have large amounts of routes and schemas, and as well as a big existing tool chain. You write your schemas using just in JSON, following the JSON schema spec, and then you pass it to this CLI JSON schema TypeScript, and it will generate TypeScript definitions for you based on that JSON. And then you can import both of them into your route and pass the interface as the generic to your route, and then pass the corresponding schema as well. And then your four properties inside of here will get the types based off this route schema and will also be validated. And you actually have fairly type safe code at runtime, it's not actually type safe, but it's as close as we can get. On the right, here's a more interesting one. And this is a fluent like API, this is called type box, and you define your schema using this type API, it's called type object, and then inside that object, you define a property with say body, this corresponds to the schema, the body part of our request. And then that body is also an object and it has a property foo, which is of type string. This schema here, what's returned from type dot object is a valid JSON schema, so you can pass that right on into your test. And then your generic here can be derived directly from that object using this same libraries static type analysis. So this one is also very cool, it's very testable, it's very dry and it helps keep your schemas more understandable 'cause they're written in code, they're not just JSON. So what if we took TypeScript 4.1, and that sort of the ability to process JSON that we've seen examples of? Combine it with Fastify existing validation part, as well as just the JSON schema spec. In theory, we could have a solution where there is no need for a generic here, where a user can just pass in their JSON data to the scheme of field. And the underlying type system would be able to infer all of the types from that JSON, and apply it through to querystring, body, headers and parameters. And this is one of the things that we're really excited to start experimenting on when 4.1 is released and it's in beta, so we've already started experimenting. But of course, we're gonna, we'll wait until the major release, until we actually put it into action. So that's about all I have for today. I would say if any of this has sparked the interest in you, please come and check us out on GitHub. We are a growing community, and we're very welcoming. If you have never made a open source contribution before, come on in, we are more than happy to help out, we have a bunch of repositories that you can contribute to with all a very low barrier of entry, all of our source code is written in just ES JavaScript, and we don't use we don't use anything fancy. Some of our plugins are written in TypeScript, which is great for TypeScript developers and of course, if you've liked the sort of patterns I've shown today, you can see all of that in more within the Fastify project itself, we ship all our own types and all the type definitions I shared today are based off of the real ones inside of the Fastify project. So thank you very much. Again, my name is Ethan Arrowood, Engineer at Microsoft. I thought this gift would be funny 'cause we're getting close to Halloween, my favorite holiday. And feel free to reach out to me on Twitter, I love chatting about TypeScript JavaScript, Node.js, and plenty of other things. All right, that's all I got. Thank you again. - Ethan, thank you so much for that. That was really insightful and I think that also builds on a talk that we had at the previous meetup, where we had I think Mano, he discussed some of these validation challenges with TypeScript, where you're trying to essentially take runtime validation and perhaps generate types from it and vice versa. And so I think we have a really, really exciting future very soon if we're able to get that, as you suggested with the template literals from the JSON schema. So that's really exciting. Do you have anything, before we get to the Q&A, do you have anything else to share about Fastify? I think it's a really exciting framework and we're seeing more and more adoption of that. - Yeah, I'd say, just to reiterate the open source community, we have a discord channel, we have discussions enabled, we have a help repository, so come and chat with us, ask us questions, our maintainer base has been growing a ton over the past year because we're really just trying to just be this great big community of framework developers. Even if you don't use Fastify, if you're an Express or Hapi or you got your own framework, I know Prisma has done some stuff with, I think the Blitz that JS folks, they're working on their own thing underneath the hood. I say, come and chat with us, we love the framework ecosystem, web frameworks, we wanna see and help each other grow. So thanks, all. - That's great. So thank you all for all of the comments, we'll now take some questions. Are you ready for some questions, Ethan? - Definitely. - So if you have a question, I think it would be a good time now to drop the question in the chat. We also have an interesting comment, thanks, interesting to see how TypeScript syntax is becoming more and more like a language of its own to construct types. Indeed, yes. And if there are no questions, I guess the talk was crystal clear. Great, okay. So now we will move on. So thank you so much, Ethan. And I think now we will move on to the quiz part and I welcome you to stay for this part, because we're gonna have questions that are based on your talk. So if you were paying attention, okay, so I'm gonna add this to the stream and okay. And so we've had already eight people join the quiz, if you haven't already, you can either scan the QR code or you can join the link that I left in the comments, and you also have a third option, developers, we love our options, and so the third option would be to go to slido.com and put that code in. And so a quick reminder that the winner of this quiz, which will include questions related to the three talks that we have today, will win a bunch of different things, will win some Prisma related swag, and we also have a Quirrel license, and if you miss the beginning, Quirrel is a task queue manager for serverless environments. So without further delay, let's get into it. Okay, I will activate the quiz and we have the first question. So what is a discriminant? First choice, a similar characteristic for a set of things, the red color characteristic from the fruits example. A distinguishable characteristic for a set of things, the orange color and the inner seeds characteristics from the fruits example. And we have the third option, a general characteristic for a set of things like the fruits characteristic from the fruits example. And lastly, a missing characteristic for a set of things, like the purple color characteristic from the fruit example. And so we have nine, 10 votes coming in. Wait a couple more seconds, and let's see if I can also show the leaderboard here. So we have the 12 votes in and most of you voted B and indeed that was the correct answer. So now we will move on to the second question, and that is, why is it hard to provide true type safety for a route handlers request instance? And so then we have the four option, it isn't hard, they're already type safe and all problems have been solved for us. Second option, there exists no mechanism for safety typing at runtime. Third, a request instance is a highly dynamic object, it can have many different shapes during runtime, it can become safer through validation. And then lastly, the fourth option, you can use typecasting thing as type to make anything type safe. And so we already have 15 votes in, oh 18 votes, okay, it's nice to see everyone joining in. All right, 58% voted for the third option and let's look at the results. So indeed the third option, the most popular one was the correct one. And so on that note, we finish the quiz for this part and we will continue. So keep the URL or keep the page open and we will continue with that later. Ethan, once again, thank you so much for joining us and hope to see you again at a future TypeScript meetup, where you might be able to share some even more exciting news about Fastify. - Yeah, thank you so much. - Thank you, all right. - This has been wonderful. Have a wonderful day, everybody. - Okay, we're now moving to our second, I will get the label correct. Yes, so we're now moving on to our third talk and our third talk is by a speaker joining us from Bangkok in Thailand, and his name is Poom. So I'll bring Poom onto the stream and get the labels. Hey, Poom. - Hello. - Nice to meet you. - Can you hear me? Nice to meet you. - Yes. Welcome. So you're joining us from Bangkok. - Yes. - From Thailand. - Yes, exactly, I'm signing from Thailand. - Great. And so I wanna give a little introduction. So Poom is a Developer Advocate at BRIKL based in Bangkok, Thailand, and he enjoys building silly apps with React. He tinkers with cutting edge stuff like Rust and WebAssembly and customizes his dot files for fun. On other days, he runs community projects like The Stupid Hackathon Thailand and The Young Creator's Camp to get Thai developers to keep hacking, or he's just relaxing at an onsen bath in some corners of Bangkok. It's nice to hear that there's onsens in Bangkok, but that's not today's topic. - Yes. - So today's topic for your talk is gonna be why you might not need advanced types. Is that right? - Yes, yes. - Cool, so without any further delay, are you ready to share your screen? - Yes, let's do it. - Let's do it. - All right, can you see my screen? - Yes, we can see your screen. - Yes. - We're getting a nice, effective, great. - Okay, does it work? - Yep, you're alive and it's all good to go. - Okay. Thank you. So I will start now. Okay everyone, nice to meet all of you. My name is Poomparin Mano and I'll be talking about why you might not need advanced types today. So my name is Poom and I'm a Developer Advocate at BRIKL from Bangkok, Thailand, and it's true, yeah, I like to enjoy onsen bath in Thailand, but most of all, I organize a lot of developer commodity projects for like students and university students in Thailand, like Young Creator's Camp for people to make their own products and the Stupid Hackathon Thailand to encourage Thai people to be with stupid things. And you can follow me on GitHub @phoomparin. So let's begin now. So the story of this talk is, I see a lot of developers, most of them they have a good grasp on TypeScript, but most of them haven't even checked out the advanced type section of TypeScript. And I'm one of those developers too. And I believe in a saying that learning in public is really, really helpful when you're learning new technologies, so I'm pretty new to TypeScript to myself. And that's why I have a question. When do I need to use advanced types? This has been a question that's running on my mind for quite some time, I don't know like when the need for conditional types or like you've seen the advanced features will come up, so today's talk is mostly for people who haven't touched a section. So if you have already, this is a good opportunity to refresh the knowledge. So in a scenario, the scenario I see, which is perfect for learning about advanced types, is building a schema builder. Like as the last talk, like ask Ethan already said, when you having like a very dynamic object like a JSON, it can be very hard to validate or very hard to get them to be type safe, so I think building schema builder is going to help me learn on this. So since I'm a Hackathon Organizer, let's make an app for grouping people in hackathons. Yay! So I'm the organizer of the Stupid Hackathon Thailand, and one problem we have is people in Hackathons usually they don't know who to group with, who to Hackathon with. So let's look like but first let's look at the API surface, let's look at the API that we are gonna begin making a schema. So when we're organizing a hackathon, there's gonna be people joining us, right? So they're going to be a member. So this is what my API for our query builder might look like. I might have like a Thai function, then construct such new type, and then you can use a function like ID, checks and number in order to construct the fields. This looks simple enough since it's just scalar types, but it can get more complicated. Like in a hackathon, you might have a team and in that team, you might have a team lead, who's just a reference or a member, or you may have the list of members, which is array of a reference to members. And this is where it gets interesting. So when you're building something like this in TypeScript, I think pretty much the challenge here is this, so not only you have to construct like a query builder in order to construct like a JSON structure, but you also have to create a type in order for the resolver to use as well. So this is the challenge for today. So how would we write this query builder just for the types? Like we don't care about the correctness, we just need the types to be correct. And this is how I would do it, so looking at this structure, you can see this is just a pure function, like a function to construct a type, and ID, a text, a number, which is like the scalar types on the top. So I would do something like this. So I would make like a text that returns an empty string, or maybe like the number function that returns an integer. So this would give us the correct type. And if you look at this, well, it looks pretty legit. We've got a type of name which is a string and a type of age, which is a number. So it looks already good, right? And even for arrays or for instance, that's already looking quite well. So what's the problem here? The problem here actually is, well, actually, when you look at the create handler function, the types here is going to be working. So it like it's going to be easy, but the fact is we are not done here. Yeah, we are not done because we're just constructing the types. But in order for an app to be functional, not only the type has to work, but we have to have the JSON structure for the schema. For example, it might look, well, I'll show you later, but for now, let's just use like a plain JavaScript object and pure functions to construct a structure. So this would be how I would do it. So I would have like a function of type and that would return to type of schema like function text, and that would return to type of string of number, and that would return the object with type of number. So while this can provide a best structure, for example, if we construct it like this, if we can post it like this with type of project and with texts and with reference to team and so on, we would get a JSON structure that looks like this. And while this might looks legit, like if we look here, well, we got the name and the type of string, we got the type of ref, so the schema here actually looks good. But the problem here is then we are failing our second requirements. Because if you look at the types here, then everything is now any. And this is certainly not fine. So even though our JSON structure is correct, our type is completely wrong. So how would we fix this? So what we need to do is we need to not only generate the type in the code, but also in the type land. And this is what I mean. So if you create a function like this, if text returns the type of string, then the problem is that string constant is not going to be there because the type is going to be widened to string, and it's going to cause problems because then you cannot figure out if the type is a number or if it's a string, and this is a problem. But this is easily solvable, so you can't just put type, it's a string, and then TypeScript knows not to widen that type. And it's precisely string. So this is the foundation that we will be building on. So, the mission here is we would need to preserve the information in our types in order for TypeScript to be able to parse that type structure. So we are already doing a good job here. We're already putting the type, like first strings, numbers, arrays, references and optionals. So now, the structure has a correct type and it is easy to go from here because even the schema and the name is preserved at the type level. So the information is not lost. So everything still works great, right? And if you look at the type, so if we hover over to the project constant, we can see that the type, the name, the schema is all there. So we've got enough information for the TypeScript compiler to work with, which is very nice. Now, if you look at another example, so this is the schema of the team, the team in the Hackathon, you can see it has the type of string and the team lead has to type of reference. Now back to our problem at hand, the goal here is to be able to create a resolver function that gets the type that we created, and it allows us to populate it. But remember, we are not creating this type by hand, we're not creating the interface at a type, but we are auto generating it from the hand stance. So how does that work? And that's our next task actually. So we have to generate the return type from our structure. So if you remember, our structure, it has like a type and then the string of what the type should be. Should it be a number or should it be a string or should it be an array? So let's begin from our scalar types. So the goal here is to map the type to number and type string to an extra scalar type in TypeScript. So you can do this pretty easily by creating an interface with a mapping and you can do like a type scaler and key off in order to construct a union. So now, we have a union to work with, which is pretty nice. So now, we just want the type field. We want to access the type in the schema, so ideally, we should be able to do something like this. We should be able to get the schema and then we assess the type on it. So this is where we're similar to a function that Lodash has called mapValues. So it just maps the values to type, which is pretty straightforward. So now, if you look at the result, now we are getting very close. Now we're getting name of the string and the age of string of number. So from a structure, now we got rid of the types for you and map it directly. Oh, but then again, if we were to map the type mapping to the native type, we are gonna get a problem. And why is that a problem? How did that happen? So the reason that it happened is because the TypeScript compiler didn't have enough information. So it cannot ensure that the type key is a scalar type at all, it doesn't know for sure, that's a scalar. So how do we do that? And the answer here is to use conditional types. So what is a conditional type? A conditional type, you can check if the type extends other type. For example, I can check if a type extends a scalar, for example a string of type string and an object of type Cthulhu. So you probably know what this result's saying. So a string is a scalar, so that's a yes, but the type Cthulhu is not a scalar, so that's a no. If that becomes yes, then you have a serious problem more than TypeScript. So now we got this scalar type mapping and now we can apply it to our type field, which the type field is just a string of number or a string of string or string of array. So now we're going to get a concrete type. So how about references? How do we view a reference? So let's look at our API surface so first. So we use like of to construct a reference object to another type. So this is what it looks like, so you can see the type in Thai script, it says that type is a ref and an item, it has the structure of the reference type. So person has a type of person and a schema. And the problem now is how do we get the items generic inside a reference? Since if we can get the item generic that's nested here, we can get access to the schema and we can map over to schema to make it to become a concrete type and that will solve our problems, right? So how do we do that? Luckily with conditional types, there's another keyword called infer. So a nice thing about infer is it allows us to infer the type that is contained inside the generic. For example, if our type of breath here, we have like a Nesta schema, which just like from another type, we can extract that. So if we try that out to get the reference item using this given structure, then we can see that, hey, we successfully got the ID and the string, and that's pretty nice. So we can just map that. Okay, now let's put off for them together in one package. Ready? Here we go. So this is looking pretty Halloweenish. This is looking a bit scary. So, to most people who haven't done like TypeScript Hallowee before, this can look pretty intimidating. But because we have been in here for a while, then we know that this is a conditional type and we know that this is mapping to a scalar, which is using the type field, and we know that the ref here is just extracting the schema from inside a nested object, and mapped up back recursively. So it gets mapped into a scalar type. So that's not too bad, but we can refactor it and make it a lot better by sprinkling some comments. Let's do that. So if we're sprinkling some comments in, we can see here, it's a lot easier to read. So we know for sure, oh, is it a scalar type? If it is, then we just map it. If it's a constructed type, we'd see that type, then we just recursively map it. Sorry, map it to our schema. And that's looking great, so now let's add a support for arrays. So what is an array? In a hackathon, we may have many team members and our query builders, a schema builder is looking like this. So we have many of member. So many is constructing the array objects, and of is constructing a ref. And now an array can contain both a reference to another type and a scalar. So now we can just apply it recursively, for example, now we have the item, which is extracted from the array objects. So now we can see that there is an item, we can map that recursively, so it can become either a scalar or a ref and then be mapped into a native type. So that solves our problems and we can see it here. So if we created a structure that looks like this in the type world type of array, which contains a reference, which contains like a schema. So when we mapped up recursively, we would just get like a concrete type, like a native type script type, which we can use to annotate our projects. So this is how the generic map boxed input to return type would look like. So if we get an input, this generic can either accept an array or a reference. So first, it checks if it's an array, if it is, then it maps and make it an array. Otherwise it checks if it's a ref and it starts to extract schema inside of it and map it again. Otherwise, yeah, just map it. So what it does is to unbox the values in the type. And if this is your first time reading a code that looks like this, I know it can be pretty difficult to wrap your head around because after the recursive nature and how you usually program in code, but now you're doing it in types. And finally, we can apply this to every field inside of the schema structure. So let's take a step back and let's look at what we did. So we have this function that maps an array and a reference, and then we have another, sorry, I'm in generic. And then we have another generic, which maps the scalar type and the constructed type into a concrete type. So if we do this and then we map it for everything in the schema, then sure enough, we are going to get a working type. So now we can see that from the type in the schema builder, we can use type of to get the schema inside after type, and then we can just do this. We can just type, and it will work perfectly, which is nice. And there goes our both requirements. So the first requirement is for the schema to be typed as a JSON structure, and we can solve that pretty easily by constructing and object. And second is by building the TypeScript type to use in our resolver, and that can be done pretty easily by using conditional types and rehearsive map types. And yeah, that's what we did. So we did it. Let's do some more practice. So we now have scalars, types, references and arrays. So now let's create an enum in our schema builder. So this is the API that you would expect to have. So it would get to your list of options and then it would allow the user to just pick one. So that would be a union, but the question is how do we turn a string array into a union? That's pretty hard to do. I know an easy way to go about this is you can use as const, so that will turn it into like a read only array, and then you can use type of notation here of number to turn it into a union. But that goes to problem. I don't want to use as const in the user code, so what can we do about this? Luckily, there's another way. So you can use this. So you can use like arrays operator to construct a choice into an array, and then you make that a generic, which extends a spring, which is precisely what we want. And this will allow us to preserve the string type so that we'll not widen the type to be like an array of string, but that would preserve like the choices in our enum. So that's great. I think now the logical thing to do about this is we can just extract a choice from this generic, right? Well, not so easily. We can see a problem here, when we do choices and index stack by number to turn it into a unit, we found out our problem. So if you remember back to the beginning of the talk, this is precisely the same problem. So TypeScript doesn't have enough information to ensure that it does what we want. So it doesn't know that choices, it's actually like a less afraid array strings. So we can hint our compiler a bit. So we can hint in and type, we can hint that, oh, this actually extends a string array. So this would be able to be indexed by number and therefore it could become a union. So this is precisely what I did, and now we can use our enums as a union type. And this is pretty nice because you can just feed it, like the choice like we did with the enum function, and we would just get a union of it. So we get another completion for all possible enum values without having to use as consts, which is nice. And now, we're running off time, so it's time for the final boss. So our final boss today is called optionals. So you might think optionals, how hard can that be? Isn't that just like making a field like not required? Okay, so let's try it out. So this is the API we want, we want our schema for the hackathon, every team should be able to have their tagline, but not every team has to have a tagline. So we would use like a box type, like con optional. So we would wrap the structure with optional and that would give us the information later that we can know, oh okay, we don't have to make that required. So what could possibly go wrong? We can just, can we do this? Like we make us generic that union separate now and define? So maybe that becomes like an optional. Well, not so fast. As it turns out, this doesn't really do anything, so both name and age is still there, is still required and not optional. So it's not very effective. And this is why I say this is a final boss, because it's pretty difficult to delve with. So let's take a step back and look at our time. So we have a type of name, which is , and that's going to be a string. But both tagline line and age is optional, so that means it shouldn't be required to include into our updates. And this is just a little bonus I found. So I found that by using the new type pattern, which looks like this, you can actually create a new type in which the value itself will still be the scalar. For example, this hello string would still have the values of the string, the properties of the string, but it's going to be interpreted as a new type. So you can use conditional types to check for that. But that didn't really help in this case, right? So I kept on searching and I found this on strangled flow. So I found this NullablePartial generic, and what it does is given a list of keys, it makes them optional. So I guess the magic here is now extends T of K, I guess that's the part that handles it, but I'm not really sure. So let's get our final boss game plan ready. So here's my game plan. So first, we will create a type of optional, then we'll unbox that optional, but if it's not an optional, then we default to never. And then we get the list of the optional keys and then we use NullablePartial so that we'll use the list of keys to make those optional. And then we can join the optional section and non-optional section back. I know this sounds pretty confusing, so let's see in an action. So step one, create our optional type, we already did that. So moving on to step two, we can unbox the optional type. So I'm using a generic here, which basically it's extracted using infer, and now we're getting a plain item. And by doing this, we can see that every field that's marked as optional now can be unboxed. And it's now just become like a type of string, but every type that's not optional, it just becomes never. And it makes it easy to check for that later. Okay, step tree. Now it's time to check for an optional keys. So now by using this filter key, this generic, what we can do is we can filter out those. For example a tagline and age, it's this and it's not never. So we know that it's an optional, so we can use this filter key generic to get a union of what should be optional so we can pass it to NullablePartial. And that's exactly what to do next. So I can use this NullablePartial generic, and then I use those two values, which is the optional fields and the optional keys, and then a pass to NullablePartial. And now we can see that they got the question mark, which means now they're optional. And I know this is like pretty overcomplicated, and there's definitely a better way to do this, but this is more like a practice to deal with a lot of generic types. And finally, we can just sign the optional and the non-optional types. And now we can see we get what we want. Now only the name is required and the age and the tagline is now optional, which is precisely what we want. Also, the extract and the exclude type is my most favorite. So basically what they do is they allows you to exclude a partial, maybe exclude like a partial, not partial, I mean, a particular union member from the list of unions, so that really helps a lot. And we're almost done here, yay. And finally, about the talk title. So you can see like what I have spoken like now has to do with advanced types. So what does the talk title mean? So this is what I mean, you might not really need advanced types if you can simplify your API surface. What I mean by this is the first time I did this schema builder, I did it with classes first. And by doing a bit classes, I ran into like a lot of difficulties, like having to use new type. So the complexity of your type depends on how you decide your API. So my recommendation is to type your API first and then implement it later, because most people, that is like a new harmer to TypeScript, they usually it's like write a hoard first and then try to type it later. And that's not going to scale. So maybe like a train of thought, maybe it might be even better if the end user just brought their own types, maybe that will get rid of the complexity. And it really depends. So I'm going to end with a closing note, thank you everyone for listening. And if you have any questions, please feel free to ask me and I will post the code to GitHub so you can try it in your playground. Thank you. - Poom, thank you so much for that, that was really, it's always great to see more and more talks going into deeper topics with TypeScript, but also it seems like one of the common problems that continuously comes back is this relationship between validation, run-through validation and the type system built on a type safety, essentially. And so this solution that you came up with was essentially an API that allows you to generate both JSON schema, which you can use for runtime validation and generate types using the generic, basically the type system to generate these types that you can use. And that was really cool to see. So thank you for that. - Thank you. Oh, sorry, I forgot to mention one thing. So one more thing. So BRIKL is actually hiring and we're looking for software engineers, both in backend and in front end, like the problems I showed you today was actually from one of my production work. So if you'd like to deal with this TypeScript advanced types, then feel free to join us. That's it, thank you. - Cool, are you ready for some questions? Let's see if we have anyone. - Yep, sure. - Okay. Let's see, anything in the comments. Well, we have a lot of positive sentiment from the crowd. So thank you for everyone who joined. And if there are no questions, we will now go to the quiz part. So I will share my screen for that and update the panel. So the last question we had was from the previous talk, and now I will advance this to the questions for this talk. So first of all, the leader of, the first place in the leaderboard is right now Simon Knott, so let's see if anyone overtakes him. And you should note that the quicker you respond, the higher the ranking. And so what does the exclude utility type do? And we already have one vote in. Let's see. So we got 19 votes for the previous question, so let's see. Yes, Benny, I will give a countdown. All right, you have 10 more seconds to get your vote in. Okay, and we are going to finish now. And so most chose the second option, exclude the given union members from the type. And let's see what the result was. And indeed, that was the correct one. Simon is still leading. Let's see, we still have more questions. And now we have the second question for this talk. Which keyword do you use to extract the type contained or wrapped inside of a generic? Poom, someone asked if you could share a link to the GitHub code. - Oh, sure. I haven't put a code at GitHub yet, I will do it after the talk, so you can monitor my GitHub profile. - Cool. All right, you have 10 more seconds And I'm about to close it, get your vote in. Okay. So most chose the third option and that is infer. And let's see, and indeed, that was the correct answer. So let's look at the leaderboard once again. So we have George who's leading and it looks like it might be George he will win, but there are still many more questions. So now we get to the fifth question of the quiz, how do we construct a readonly string from a list of strings without the use of as const? So we had a question from the audience from, Guy S, who asked, do you use Open GI in BRIKL? - Well, yes. We actually use . So yeah, we use a lot of 3D technologies. - Thank you for that question, Guy. Okay, we have 14 votes, 10 more seconds to go. Okay, let's look at the results. So by the use of the readeronly minus readonly keyword, and most of you got it wrong, by the use of rest operator in a function argument. Let's look at the leaderboard. So we have Soumya geekySRM who's leading right now, and I believe that we have one more question from you. No, I think that was the last question. So on that note, Poom, once again, thank you for your talk. Hope to see you soon at another TypeScript meetup and in the meanwhile, stay safe and take care. - Thank you so much. bye bye. - We are now coming up with the third talk of today's meetup and we have a very, very special guest who's joining us from Potsdam in Berlin. And so we have Simon Knott joining us and I'll bring him onto the stage. - Hello - Hello, welcome. Let me just get the labels working. Okay, so Simon, welcome. It's nice to have you here. - Thanks for having me. - So I'll give a short introduction and then hopefully, we can get started with your talk. So also bring your slides onto the screen. So Simon Knott is a Maintainer at Blitz.js, he implemented SuperJSON and currently builds Quirrel a task queuing solution for serverless deployments. He also created EntE, a digital absence accounting solutions for German schools. And when he's not working on development tooling, he loves riding his bike and deejaying. And sometimes he's also found studying at Hasso Plattner Institute in Potsdam. So Simon, your talk for today is fully typed fullstack development using Blitz.js and without any further delay, I'm gonna give you the stage. - Thanks, Daniel. As Daniel said, in my talk, we're gonna have a look at fully-type fullstack development. And it's gonna be two parts. So at first, we're gonna take a look at this JS and especially one aspect of it, then we're gonna take a step back and have a look at whether we can spot a trend in the web development ecosystem from what we've seen in Blitz.js. So I've got a slide that tells you who I am, but Daniel already did that, so we can just skip it. Let's just get right into what's Blitz.js? I guess most of you haven't heard of it. Blitz.js, it describes itself as the fullstack React framework. So it's a framework used to create React web applications, as opposed to say web pages. And it's built on Next.js, which is that awesome React framework by the team ad for sell and takes heavy inspiration by the mother of productivity, by Ruby on Rails. The main fabric proposition of Blitz is that it makes you very productive, it really supercharges your productivity and I can personally vouch for that. And one of the ways it does that is by the so-called Zero-API Data Layer. And that's what we're gonna take a look at today. So the first thing, we're gonna need to install Blitz. Similar, is easy, just run npm install, global blitz, and then you've got the Blitz command line interface, and then you can use Blitz new and then the name of a project to scaffold and you baby blitz project. In this case, we're gonna build the TS meetup page. So this is a page for this meet up here where the upcoming meetups can be seen and people can say well, I'll participate there, and just like a simple demo app. So Ron Blitz Muti has meetup page to generate that and then changed into the directory and run with start to start your development environment. And now again, what we wanna build is that meetup page and the first thing we're gonna need is a page or a route called slash meetups, where you can see the upcoming ones. And to implement that, we're gonna create a new file, which is at pages/meetups.tsx. That is just like a next year's convention, whatever file is placed in the pages folder will become a new route in your application. So that file will be accessible at slash meetups. And it's a very simply React component. You don't need to understand React for this talk at all, just know that this function, it fetches the upcoming meetups from say your database, and then it renders them in some list. But the question is, how does it fetch the upcoming meetups? How does it do that? And back in the old days, when we had like server side rendering, maybe with PHP or like anything that's older than a couple of years, that was easy. Everything's rendered on the server, so just call your database, you've got access to it anyways. But now recently, client-side rendering has become quite a rage and you'd also want to get the new meetups or get your upcoming meetups from the client's side. So now, it's obvious that you don't want to access the full database from the client side, so how do you get the data? And you may... The obvious solution to that is, well, just create some API that the front end can call, but just create some IPI, like there's so many choices. You can create a REST API, you can use JSON but you can also have like in the GraphQL and points. You can use good old RPC, but like if you use GraphQL, do you'd use a pollo or Relay? Maybe you don't have an API at all, but put your data into some, use accessible database, like FaunaDB or Firestore. And even when you have that, you just use REST, how do you fetch the data on the front end? Do you use like windowed or fetch to use some accompanying tools like React Query or something? There is a lot of different choices available. And to be honest, it doesn't really matter what you decide to use. Whether you use REST or GraphQL or something like this, well, it matters from a development standpoint because it impacts the way you work and it impacts the development speed you can work at, but it wants, like there's no user coming to you and saying, I love that you use GraphQL at Facebook because it makes my pages super fast, like your users want to worry about, or won't care about the underlying technologies. And to be honest, this is just a lot of different choices and a lot of headache caused by what kind of transport you use. And this is also not easy, you still have to do like a lot of thinking about what would be the right solution. And even when you decided for something, let's say you're gonna build a REST API, you always have to decide what's the right name for the routes? Do I use get, put post patch, options, hats, make call, or what do you use as HTTP headers? How do my HTTP headers work? So how would this look like if it were easy? That's the question that Brandon Bayer, who's the original author or the original creator of Blitzjs, that's the question he asked himself. Like if it were as easy as possible, how would this look like? And to be honest, if this was easy as possible, you just import your backend function from the front end and called it like any other function. You wouldn't want to worry about transferring the data over the network. And that's actually what's possible with Blitz.js, that's the so-called Zero API Data Layer. So let's get back to our page. And as you can see in the second line, we import a function called getMeetups from query/getmeetups. And that is actually a function that when run or when called, will execute on the backend, it doesn't matter if you call it from the backend or from the front end, it will execute on the backend. So it has full access to the database and to the full Node.js APIs. And you import that, and then in the first row on the first line of the component, you can just call getMeetups. And that's how it works. At least conceptually, like in reality, you still have to import like some helpers from Blitz, like useQuery, and then you're gonna have to wrap it in useQuery. But that's just to make TypeScript types work and all the rest it's like it works. So yeah, you get the concept. And to show you like this is not some weird trickery of the the Queries and getMeetups thing, if we have a look at that, getMeetups file, this is query/getmeetups, this is just normal TypeScript code, right? That's this function called getMeetups that uses the database clients to find the upcoming meetups and then it just returns it and then that function is exported as default. And that's all, that's the function that's important here and it works. And actually, that import db from db that you see there, db is actually just an alias, it's the default alias on Blitz.js for a Prisma client. So we're going to take a look at Prisma who you may know, because they are the organizers of this meetup, we're gonna take a look at that later. And because this is just normal TypeScript code, there is full TypeScript support. Here's a screenshot from VS code, where I can see how this fully supports like full editor integration and a full auto complete, because the code is just like, you just import the function and you decode it and actually those two auto suggestions you can see there, these come from the database clients. So they're fully passed through. And that is something that doesn't happen with traditional backends and over traditional API styles of things. And that's because normally, the type soundness of even the four types with projects, it's lost in transit when transiting over the network. When I first saw this, I was like, wow, this is amazing. It's just so easy and you don't have to worry about all those things. And now if you're like, oh, I feel like me, and you're like, wow, I need to check this out, really go check it out. Blitz started at the beginning of this year and it's been around 145 contributors in STEM. We started like, yeah, we started at the beginning of the year and we are about to hit beta in the coming days and plan on moving to version 1.0 by the end of the year. There's already some production plates apps out in the wild, for example, the Quirrel landing page is built in Blitz and I can vouch for it and it makes you very productive. So let's get back to that magic trickery with importing the backend. And I can see that in the chat, you're already debating about how this works technically, and I think, let's see, yup, it seems like you're correct. So let me tell you how that works. You can see here that there is this import from queries/getmeetups. And whenever you import a file from a folder that is called queries, so quarries is a magical name in this context. Whenever you import a file from a folder called queries, or your patients for that matter, we will take that import and replace it with a function that calls the backend to execute that import. Again, so whenever you import from there, we're gonna replace it in a compile step, we're gonna replace it with a function that calls you backend. So we generate the API for you. That's why it's called the Zero API Data Layer. For you as a user, you don't have to worry about APIs or for you as a developer, API is none of your business or at least nothing you need to worry about or think about. And then the API is auto-generated for you. And depending on queries or mutations, we do some things differently to have it work well with caching and stuff, but the concept is the same. And that concept, can be called cogeneration. Cogeneration is the process of taking some out effects, for example, code and transforming it into code. And that is actually a thing that Blitz uses to make develop experience so good. And Blitz is not the only thing to use cogeneration. Another tool that uses cogeneration heavily is Prisma. I promise we're gonna take a look at Prisma and that's what we're gonna do now. So Prisma is that database client but it's a nice company who organized this meeting. And it works like the following. So you specify your Prisma schema in Blitz apps, it's at db/schema.prisma, and the Prisma schema is a file where you put in your data module for your application. There's this like a Prisma specific language, and you can see there's two models, one for meetup and one for participants, and both have like an ID and a name, meet up set for date and then there's like a many-to-many relationship between those two. And you take that model and then you run Prisma Generate or Blitz DB migrate for that matter, let's encapsulates it. And when you run that, Prisma takes your artifact, which is the Prisma schema, it does some transformations and it generates this code for you. So this is the generated code again, cogeneration. So it takes the schema, and for example, it makes this type out of it, the meetup type of like an ID and the name and some other properties, and it also generates the meetup delegate, which is an interface that contains like data access or database access functions that it has like find one functional, where you can find exactly one, or I think now it's called find first, we can find one row in the database has like an update, a two lead function, all that good stuff. And it even has custom made comments. So this is really next level develop experience because this is normal TypeScript code, your VS code can fully give you editor support. That's what we've seen a couple slides back. And the interesting thing about Prisma is it generates into your node modules folder. So this file lies at nodemodule/.prisma/client. So this is also cogeneration, and turns out cogeneration is used in Blitz and in Prisma, but not only there, but also in these well-known things. Let's start from the top. Babel, I guess you haven't heard about Babel. Babel is like the mother of cogeneration or the mother of co-transformation. Babel takes modern JavaScript like ES next, ES7 or are we at ES9 yet? I don't know. And so you take a modern JavaScript version and you do some transformations to transform it into an old JavaScript version like ES3 that even Internet Explorer can understand. And you can also do like custom modifications, which is the very interesting thing about Babel. It's pluggable, so you can have, for example, your own Babel plugin, as an example, I created a Babel plugin for Next.js and Super JSON users that wraps all of your Next.js routes to use Super JSON by default, without the developers to do anything. That's what you can use Babel for. So take modern JavaScript, make old JavaScript out of it. Then there's Tyrant CSS. Tyrant is a lot more icon to something like Prisma. You take your table and config where there's colors in there and like custom gapping sizes and paddings and all stuff. And then it generates your own tailor-made custom CSS framework for you. There's stuff like SaaS which is basically the TypeScript of CSS, which lets you ride CSS at a higher level and then SaaS comes in and compiles that back to CSS. So take SaaS code and generate CSS code out of it. Again, cogeneration. And then stuff like Next.js. In the Next.js, your artifact that you put in is the flow based routing structure. So any file and the pages directory is a route, the Next.js there's some transformations and some generation steps to generate an arguably more complex version of your app that fully supports server-side rendering clients that routing and all of the data fetching methods that Next.js also offers. There's also cogeneration. And there's a lot more examples, like a Webpack or even the JSON schema to type scripts package that Ethan talked about, which is also great, which takes JSON schema files and then transforms it to TypeScript, also cogeneration. But it's awesome, but it has its problems. And the biggest problem at least for me, is fragmentation, which seems to be like an overarching problem of the JavaScript ecosystem, I guess. But the thing is that there is no agreed upon a way to do cogeneration. Everybody builds their own tool chain. I mean, TypeScript has their fully owned puzzel like let alone their puzzle is 8,000 lines. Then there's like a Babel which uses a modification of icon for the parser or Webpack, which uses normal econ, and then a lot of other things. Like Blitz has its own, NEXT has its own, they all do their own kind of tool chain. And this comes with performance issues because building a cogeneration tool chain is in a lot of ways very akin to building a compiler. And we all know building a compiler is hard. So yeah, performance issues. Also limited portability. So you can't just take your Babel plugin and move it to say Webpack, which makes it hard to move to new projects, to move to new tools in your tool chain. Which brings me to the next problem, like there's no agreed upon standard to do cogeneration any way, so some projects generate into node modules like Prisma does, then there's stuff like JSON schema to type script that generates into your source directory, and then kindly ask you to at their files to look getting more, there is stuff like Tailwinds, which generates its output code into some hidden way pipeline thingy, so that the Bandler can use it, and a lot of different ways. And this also results in poor editor integration because there's no agreed upon standard, no editor can accompany to that. And the only way that the awesome editor support of Prisma can work, is because it generates real files, like files in your file system. And that has its problems too. Like part from that, there's user generated files in Node modules, which to be honest, shouldn't belong there, there's the problem of people forgetting about regenerating their Prisma clients, which I am suspect too as well, like I do it all the time. I always change my model and forget to regenerate the client. Also not having those standards just makes it hard to build in additional tooling on top of it. So it would be better to have like an the official JavaScript standard or at least something like the industry standard. And in the Java X system, this is something that's been there for quite a time. 16 years now since the annotation processing API has been introduced, and annotation processing is actually something where Java code can hook into the compilation process and it's in a wide use across the Java ecosystem. So you may have heard of Project Lombok or of Spring Boot, both of them make heavy use of annotation processing, and I'd say that most JavaScript frameworks use it. Also C# has introduced support for Source Generators earlier this year. So yeah, that's also something where people are taking a look at cogeneration. So let's see how we could fix this problem in the JavaScript ecosystem. And to see how that could work, let's have a look at a conceptional view of our current tooling. So this is a conceptual view of Babel. Remember the tool that takes the modern JavaScript code and translate it to old JavaScript code. So it takes some source code, like modern JavaScript code that then it Lexes and Parse it. So Lexing parsing is like the technical term for making sense of your code and building a so-called abstract syntax tree, which is like the representation of your program. And then after Lex and Parse, it will put that into the transform step. And transform step in Babel is called plugins. And so a plugin is a function that takes, abstract some text for the code, does some transformations and returns it. So these plugins are what allow to take modern JavaScript code transform it. And there can be potentially a lot of plugins and you can even use your own custom ones. And then after you transformed it and you have like your final output, you take that and use it to generate output. Then there is this conceptual view of TypeScript. So TypeScript, you also take some source code, in this example, you take TypeScript code, then you'll lex and parse it, and then that's the interesting thing about TypeScript, you check types. You've got that type system and you do some types on, just checking and stuff like that. And then conceptually, that's the point where the editor integration happens, after checking types so that your editor knows whether the types are correct on not. And then after we check types, we're gonna again transform it, this time, this transformation step is not user plugable, but it depends on what your types group conflict says. So when your deployment target it's like ES3 or ES5 or something like that, it will transform a lot, like to ES3 or ES5 but if it's like ES next, it will only strip away the types. And then that again takes and generates output very similar. So what would it look like if Babel and TypeScript joined forces? If TypeScript edit would become better when it comes to cogeneration. Because like we've seen, Babel does cogeneration well and TypeScript does not. So this is how it could look like if they joined forces. Again, take source code this time, not only JavaScript and TypeScript, but also JSON Prisma schema files, Tailwind configs, and potentially even like images or something, any file, you go and Lex and parse it, and then you pass it to this pre-type checking transformation step. So this is similar to the transform step in Babel as in it's user configurable, it's plugable. And in this step, that's where our cogeneration happens. This is where for example, Prisma is and Prisma picks the incoming Prisma schema file, and then generates outgoing like it's Prisma client and gives that back into the pipeline. So that's when the transforming is done, check types will have access to all of the generated files that have been generated by the Prisma compiler plugin. And for JSON schema to TypeScript, this will be even easier. So what you could do is you could take some JSON file and transform it to TypeScript files, and then in check types, you could not have to import from a different file, but in your TypeScript code, you could just import a JSON and it would export some type. And because the editor integrations, what happens here, it would work wonderful for developers. And then after checking types, we again transform this time, like similar to in the TypeScript step, transform to the target platform, ES3, ES5. And if you can get like web assembly potentially, then generate the output. So this is nothing that I know the TypeScript team to be working off, this is just my own weird thought. I love to see something like this and personally, I think it would be useful to the community because it would greatly simplify the process of cogeneration and does make develop experience a lot better in the process. But this is not the only idea. I've got an excerpt from the block post on "Introducing Rome" here, by Sebastian Mackenzie. And Sebastian Mackenzie is well-known name because he's the one that developed the original version of Babel. So the Babel plugin or the Babel project that pretty much powers all of our JavaScript tool chains at the moment, except when it's using TypeScript, that's the guy who worked on it. And in this blog post, which is a couple of days old I think, or like a couple of weeks, he says, "Language tooling maintainers spend so much time working on the same things. But processing codes, a processing source code, whether it's in a transpiler like Babel, linting it in ESLint or bundling it in a Webpack is fundamentally the same problem with overlapping responsibilities and technical implementation. A linter in the JavaScript ecosystem is exactly the same as a compiler. They both consume code, then produce compiled code and errors." So that's what he says about like the current state of JavaScript tooling. Basically, he says we all do the same and we work on our own projects, why not join forces and all work on one project instead? And that's what he's doing. So Rome is the project he currently works at, which is meant as like a spiritual successor to Babel. And it's kind of a unified tool, it does linting, bundling, compiling, type checking and all of that stuff for you in one package, and thus it wants to be more efficient and just a better experience, and that what he's currently working on. I think it's financed by Facebook, not sure. But that could also be the solution to the cogeneration problem. Like if Rome had a user plugable cogeneration step, that would solve the problem, which only needs Rome to have support for the language server protocol, then that would be it. So that's what Seb has to say. All right, we're coming towards the end of the talk. Let's do a quick recap on this. So in the first half of the talk, we've seen Blitz.js or at least parts of Blitz.js. Blitz.js is a fullstack React framework. It has awesome developed experience at least from what I'm concerned, and it's the community maintained project. You should definitely check it out. I say that really like in deep love for Blitz.js, it's awesome and really come by it. We're very welcoming, very inclusive and overall, nice community of people helping each other and sharing a common goal of goods, JavaScript or goods on React future could react to them. Blitz.js uses cogeneration under the hood just as a lot of other tools. Cogeneration has become more and more relevant in the recent years, especially in the JavaScript ecosystem, it has been a normal thing in other ecosystems, but in JavaScript, it has become more and more relevant. It can be used to supercharge develop experience for example, in Blitz or in Prisma, and it's a huge driver behind the recent tooling improvements that we've seen. Still, there's some room for improvement in cogeneration, and I'm looking forward to see what will be there in the future. I originally had like a shameless plug for queue in this slide, but given how often Daniel has talked about queue, I think we'll just skip this now. And now we're gonna say like, that's all, folks, that's been my talk. And now I'm looking forward to questions from you. - Sorry, my microphone was on mute. So thank you so much for that talk, that was really insightful. I think that was a really good introduction to Blitz and also to a lot of the shifts that we're seeing in the JavaScript and TypeScript ecosystem at large. So exciting things, and indeed now is the time for questions. So if you're listening and if you viewed the talk and you have questions, now is the time to drop your questions in the chat. Thank you for the comment. And if there are no questions, I think we can go on to the quiz before we wrap up for today. Let's get on with the quiz. So we have some really positive feedback. I think one of the nice things that I've noticed is that, oh yeah, we have one from Emma. Amazing framework, haven't heard about it before, it looks promising. Is it production ready? - Yeah, that's an interesting question actually, because what I would tell you from an emotional kind of standpoint is different what like the rational assignment will tell you. So if production ready really means like your big project where you tons of tons of money, no, it's not. It is currently still an alpha, I think I forgot to mention that. Currently it's an alpha and we are moving to beta in the next couple of days actually, and aiming to go to 1.0 by the end of the year. However, I am currently using Blitz and I know a lot of other people are already using Blitz in their own projects, also in production. So for example, the Quirrel landing page and the Quirrel dashboard, which is the place where you can put in your payment details and get like Quirrel API keys, and then see statistics that is fully developed using Blitz. And it really makes me productive. And then there's also some other smaller tools that have been developed using it. So for example, a couple of days ago, there was the React summit conference, you may have heard of that. And they had some conference badge application that we actually found out was built using Blitz. And at the moment, that's like the prime thing because it's not that important, but the cool thing about Blitz is it makes you super productive also on these more fun projects. So yeah, is the production ready? Kind of depends. So for me, it is for my own projects, but I'm also level two maintainer and whenever there's a problem, I mostly can get it fixed myself and provide a buck for other things I didn't know. But yeah, if you're doing strip, definitely check by at a Slack and people will answer all your questions you have. - We had another question coming in from George. And I guess this is also touching on probably one of the biggest value propositions of Blitz, and that is that there is no sort of like abstracts the API layer for you. And so George is asking, how flexible, configurable or plugable is the transport layer? - So I'm not sure if I fully understood that question because the way I see it, that the developer position of Blitz is that it abstracts array, that transport layer. So making it configurable is not really something we're interested to do, we're interested in providing like the one solution that does everything. There may be things that, like I'm not really sure what you mean by that, if it's stuff like I have my own data types that I want to be transferred correctly, that's something we've got figured out. So Dan has mentioned that I'm the code owner for Super JSON, which is Blitz's own JSON civilization framework. So it basically takes some JSON and generates another JSON out of it. That's again cogeneration basically, but this time in runtime, generates another JSON out of it and adds some annotations on what the original types were, so that when we transfer it over the wire, we can then reapply those annotations to preserve the types of things. Because so maybe you know that when you, JSON dot string fire a date object, it will be downgraded to a string, but Super JSON, it won't. So it will be downgraded to a string and then get the annotation attached and then afterwards we just reapply the annotation to make a string out of it. That's something we do in the transport layer that may be interesting to you. - All right. And now I think is the time, we had one last question, maybe you can give a short answer to that. Is there an example of a mid-size or large-size application built on Blitz? - Well large size, I don't think so, mid-size, maybe check out the Quirrel webpage, which is open-sourced or take a look at the Blitz Wiki. There is a list of apps that are in production running Blitz and there are something like a Blitz space job board, and some like startup landing pages and start up applications that could be considered mid-size I think. - All right. Well now I think we're gonna do the finish up with the quiz. George did have a reply, but I encourage you to take this and continue this conversation more so. - George, just message me on Twitter and we'll talk afterwards. - Great. So now we'll bring back up the last part of the quiz for which we have an award. So we have Soumya who's leading the leaderboard and we have two more questions. And so we will now move on to these last two questions. And so we had the question, who is the original author of Babel? We have two votes already in, three votes. Some very interesting choices there. - What do you mean like, Douglas Adams? - Yeah. - Yeah, because like the Babel fish? - Right. Okay, 10 more seconds and we're gonna close the vote. We have one more coming in, and we're gonna wrap it up. Okay, let's see. So indeed, it's Seb Mackenzie, and we have Soumya who's still in the first place. And now we're moving on to the last question. And so the last question is, what is the name of Blitz's original creator? Five votes, we'll give it 10 more seconds, enough time to Google it. All right. And we're gonna wrap it up, and the voting is closed. So indeed it's Brandon. - That's kind of a good result. - Yeah, so we have a winner, Soumya geekySRM. So congratulations, you won a lifetime subscription to Quirrel, the serverless queue manager, and also a lot of Prisma swag. We'll send you a t-shirt and all of the other goodies, I suggest that you reach out to me via Twitter so that I can get your address and everything. And so on that note, I'd also like to thank you Simon for your insightful talk, it was a really introduction to Blitz. - Thanks for having me. - And do you have anything else to share before we wrap up? - No thing, it was a nice evening here. - Cool, so thank you so much, Simon. Enjoy the rest of your evening. - Yeah, thank you, you too. - So we now come to the last part of today's event. And for that, that like to announce that we also have a new meetup that we're starting, and that is I'm gonna share my screen once again, but the name is going to be called Advanced TypeScript Trickery. So let me just share my screen here. So the meetup, you can join it, I'll drop a link in the chat. And the idea is that this meetup will be a bit of a smaller meetup and it will happen on Zoom, and the idea is to create like a gathering around like a virtual fire to chat about some advanced type group stuff. And so this will be moderated by Tim Suchanek. Some of you may know Tim Suchanek is a software engineer at Prisma and he's is leading a lot of the TypeScript efforts. He also gave talks at TS Conf recently, and he's been very active with the TypeScript community at large. So I think these will be really exciting. And if you're interested, you can join the meetup group. And all of these talks will also be published on YouTube after. So you'll be able to access them and they'll also be transcribed, so there'll be accessible. So on that note, we had about 70, I think at the peak we had 80 viewers tonight or today, depending on where you're joining from. I'll open up my DMs, I thought I had DMs open, but I'll open them up in a second and you should be able to send me your email. And yes, so thank you so much for joining, thank you to the speakers, that was really great. And hope to see you here again for the next TypeScript meetup. So goodbye.