(soft music) - [Host] Okay, yeah, so
how many people are we? 19, not bad. So, welcome everybody. Today, we wanna try it in Zoom
to have it more interactive because with StreamYards, the
software you used last time to stream over YouTube, we
have to manually lift you into the stage, but here you can just talk if you have questions. So yeah, welcome to the second episode of Advanced Typescript of Trickery. And today we, again have a
bunch of people already prepared who want to share some
awesome TypeScript tricks or trickery with us, but also in general, we are always still open for
people to spontaneously appear on the stage, so to say and
share something interesting, can just be like a five minutes trick. Doesn't have to be like a
polished presentation or anything, anything that you're excited
about that's awesome. So, yeah, let's start. So we have already a couple of people, so to say, on the stage. Here it's not as in the previous episode where we are a bunch of people
on the stage, but still, so whom we already have here whom I know is Simon Knott. He has been here last
time in our last meet up. So Simon the founder of Quirrel
and working on SuperJSon Blitz and all kinds of awesome stuff. We have Pierre-Antoine here
who is author of ts-toolbelt. So he's is very advanced in his TypeScript and will also later share something. Same for Simon, Simon
will also share something. And then we have Emil Fugaunot
or also known as Sutton for the people who know him. On GitLab, he is one of our
smartest and most active community members of the Prisma community. And he is also working on his own startup called Graph Metrics. So welcome everybody
and thanks for coming. More people are coming. so if
you miss anything, no problem. We are recording this and
we'll also upload this. I would say we just start with Simon. Simon, how are you doing? - I'm fine, thank you. Yeah, it's awesome to be here. And I was asked to bring some
interesting TypeScript stuff. So I thought about problems
I had and I found one which is actually has
to deal with SuperJSons. So let me share my screen. SuperJSon, if you haven't heard of it, it's a project that was born from Blitz JS which is a JavaScript framework
and what SuperJSon does it serializes JavaScript types to JSon. And it's basically like an
extension of JSon on Stringify. And one of the things that
we struggled with a lot is being compatible to pre ES6. So we want to be compatible
with Internet Explorer. And I think like IE6 is the thing that's the problem, I guess. I don't know really because I haven't had to support IE6 until now. And that's where the problems come from. And the basic problem we had is we used functions like object.values and that is not available in
pre ES6 JavaScript engines. - [Host] So you need to qualify basically. - [Simon] Yeah, and like the thing is we built our own wrapper
function around it, which now called values of
object and that just drives, if there is values and object available, it will use object.values,
and if not, it will fall back. And we can do this
because in our TS config, we say to include or ES Next libraries. And that's why TypeScript
allows you to use object.values. If we downgraded that and
just said, instead of ES Next, let's just not have ES Next in there. It would, I don't know if it notes... It's not live updating, but
it would not allow this. And the basic problem that we're facing is we want to have this at a modern setting because we want TypeScript
to actually know that there is object values available. But at the same time, the
thing with this year now is that TypeScript will throw, like if you don't disable all this stuff. I didn't know what I did to disable it, but if you don't disable
some settings and TypeScript, it will warn you because it will say, "Well, there's always values and object. It won't be missing any time because you have set it to ES Next. It will be available. And the basic problem
is we can either choose to lower our target to something that's actually ES 6 compatible
and that's ES 5 compatible. And that way we won't be
able to use object.values at least really TypeScript compatible way. Or we raise our targets up to ES Next and that way TypeScript
won't be able to correctly assess our available
functions in the target. And that is really the base problem. It's about supporting
multiple JavaScript targets and this is just like a
question for all of you, but like have you encountered
similar problems before? What did you do to solve them? Do you think there's like, is
there some TypeScript feature that I could use for something like this? What are your thoughts on this? - So my naive approach would
be to just lower the targets to put it to the minimum
I need to support. Then I'm done- - In theory, it should transpile it to... it should transpile the object.values. - All right, but there may be cases where we don't want to use to transpiles one. Say, I think we've object.values,
it's not big of a problem, but there may be new standard functions that actually implemented natively and that are much more performance while we want to use the new ones and only use our polyfilled
version as a fallback. - I don't know how TypeScript... Have you checked out how
TypeScript is actually polyfilling like something like object.values? Maybe they are checking for
existence of that feature, and then you would be fine. - We could try that, right?
- [Host] We could try that. But I totally get your point, you don't want to
unnecessarily polyfill it if it's natively available. - [Simon] So you say,
if we lower our target, I think target to ES 3. - [Host] You can keep
the ES Next in the... Yeah, yeah. - [Simon] Yeah, I that
was a problem, right? We just need to put target to ES 3 and then it won't work anymore. I think if we put it to
ES 6, then it will say... We load TypeScript. That was one, then it will just say that- - [Emil] In theory, it
should only warn you if your live does not
have values and object, but the target doesn't
affect the warnings. - [Simon] All right. Yeah, so if we lower the target to ES 3 and run everything around build, then we can see that in this slash... What was the file U2.JS, the added one. It will... Values of object, it will not
really transpile that one. Because it is available. Maybe because it inferred it. If we try this entrance pilot - [Host] But we had the same
problem with flatten or flatMap Their TypeScript was not
successfully transpiling it, although, we had target ES 5, and then we just use the
polyfill for that ourselves. So maybe it's also a TypeScript back. But from my understanding, if
you set the target to lower than where are the features available, TypeScript will polyfill it for you. So it might be a back- - It's kind of a weird problem
because with other compilers, you don't have to support
multiple target platforms. That's very much a JavaScript thing. - Yeah. Yeah and I think in Babel,
you can then say, what is it? Less than 5% or something, right? - Yeah. Yes. - [Host] Yeah, it would be interesting. I'm pretty sure that's a
issue of TypeScript somewhere to make this easier. If not, then you should create one. You get my up vote for sure. And so SuperJSon, is that running in the browser and to nodes? - Yeah. Yeah, it's fully... What's the cool word for it? Isomorphic, yeah. - Yeah. Okay, nice. Yeah, that's definitely, I think a problem that many library authors
are struggling with. Pierre-Antoine, in case
you are here right now, how are you dealing with
TypeScript versions? I guess, in ts-toolbelt, you also require like certain TypeScript
versions to be there. Do you just set that in the package.JSon or how do you make sure that people use the right TypeScript version? You are muted by the way. - I actually use this
code most of the time. Okay, so yeah, I let the
people do as they wish. The problem is when they do
that, they can break stuff. I don't want them to use
the TypeScript version (indistinct). - Okay, but like, when
you say for ts-toolbelt, you tell your users that they
need a minimum version, right, of TypeScript that they can use it, right? - [Pierre-Antoine] Yes.
- Yup. Yup. Yeah, we have the same for Prisma Client because we recently
added some fancy features also with the help of ts-toolbelt. And you need a TypeScript for that one now and you get a cryptic error on 39, but is there like a standardized
mechanism for a package. For example, in its package.JSon to say which minimum TypeScript version it needs? - Yeah, you can use the peer dependencies but a lot of people complain about that. - Yeah, peer dependencies
would be an option maybe, yeah. - Maybe there should
also be like something like an "engines" field for
the TypeScript compiler. - Yeah, exactly, because
that's also what we use for node, the "engines" field. but it seems like the packege.JSon
is the wild West anyway. The spec there, like if
specs are around there and people just add some stuff. Like recently the React team
added that you can provide where you have to serve
our components and so on. Like if you have a package
that can expose the server components part
and the client part, they also added that, for example. Anyway. Yeah, that was cool. I mean, that's really a problem that I think many people have, so you can also keep us posted if you find the solution for this. Awesome. Yeah, I would say we
continue with Pierre-Antoine. So Pierre-Antoine, for the
people who don't know him, he is the author of
ts-toolbelt, which is one of the most advanced
TypeScript libraries out there on the type system level. And he has also helped us
a lot in the recent weeks with Prisma when we made our
types a bit more type safe or sometimes also just
more better DX and yeah. Pierre-Antoine. Do you have something you
want to share with us? You are muted. - (indistinct) it's horrible. Share. Does it work? - [Host] Yes, we can see your screen. - [Pierre-Antoine] Okay, so a
problem that a lot of users, people usually need to access... How can I say this? Properties that are
nested within each other and actually today in TypeScript it's quite difficult to this. So someone gave me a
challenge to be able to type and get utility to access properties and its type state at the same time I'm just going to put
the get utility here. You can take a look at it. So there's get utility is quite simple. You give it an object of type
O and you give it that path. And then they get utility
returns you the value of the type of data from the figureheads. So that's go prop=get and
then we have an object. A user object for instance. and then you get, automatically, you get auto complete on your objects, like on the properties, like
you have a friend's property and you can look the props of that user 'cause friends or users obviously. And then you can access any kind of... maybe you have a thousand friends, so let's go look at the
last friend you have. - [Host] Oh. - [Pierre-Antoine] And it tells you, okay, it's a user that can be undefined. But maybe it's not undefined,
so we can as well continue. That friend that's sitting at the 1000, you can look at his name and
then it continues working. Or we can even do more friends. Friends and then continue so on and so on. - [Host] This is next level shit. - [Pierre-Antoine] And the tricky part is that it's auto completes. It doesn't matter how deep you go, it will continue auto
completing your types, and that was not possible before. But there's quite a few tricks. I'm going to say I spend
maybe three days on this. So okay let's go through this. The actual magic is going through AutoPath this far in the utility. You give this AutoPath, you give it the object you are working on. But in this case, it's the user. And the path that you are typing is going to get passed to AutoPath. And then AutoPath is going
to basically try to go to that path as you type. And when you press that dot, it will give you the next possible path. So if I type something stupid, obviously it can give me anything next, and it's even going to highlight me that it's not a valid path. So let's look at this AutoPath. I'm not going to retype it
because it's so freaking complex. So it's quite simple,
actually, the principle. we pass AutoPath, we give
it a call the object, the user object is going
to be called in here. We give it the object, we give it the path
that we are busy typing, that's going to be complete. And we split that path
that we're busy typing. We're going to split it in such a way that we split it on each dot. So when we go back here, we
have friends.1000.friends. Then it's going to be split
into an array, actually, that's going to be friends then 1000 then friends and 50 and so on. And thanks to this, we're
going to be able to go in each part of the path and access it. So let's look at ExecPath. ExecPath is quite simple,
it's what I explained. It uses a path utility. I'll demonstrate it here,
it's going to be easier. (mumbling to himself) The path utilities are recursive type. I'll show you just now. Or you can give a split
path, like I told you, and it will give you the... it will give you the type
that sitting at that path. I could give them number as well, yeah. It's in an array. And then we can continue. It does exactly what the AutoPath does, but the AutoPath does
something extra is that it auto completes you as you type, when you praise that
dot, it auto completes. Okay. So when we look at that ExecPath, it actually gets the next
path compared to typing tabs. So if your path is a valid path, this path utility is not
going to return you never. And because of that, we'll
be able to get the next path, which is encoded in a MetaPath
that I'll show you just now. Let's take the MetaPath of a user... MetaPath, User... It's a bit abstract, but the
basic principle here is that it takes the object user,
one for each property, it will inline the name of the property on the property, directly - [Host] Into a union. - [Pierre-Antoine] That way,
when we access the property, we can get obviously the
name of the same property and we can continue deeper. Let's say, if I do MetaPath<user> and then I access friends, then the next MetaPath can
be accessed, let's hope. (muffled by audio static) under the name. I don't know if I
explained this, it's a bit- - [Host] What is the O again? Can you show that?
- [Pierre-Antoine] What? - [Host] In line 32, what is this O? - [Pierre-Antoine]
(whispering) Oh, line 32... - [Host] The last... Ah, you're just accessing
the first element, sorry. - [Pierre-Antoine] But
basically what MetaPath does, it goes all the way through the objects. Even if it's recursive,
it inlines the path in such a way that we can dive in it and it prepares the next up by concatenating the previous path. (audio static) - [Host] We have... - [Pierre-Antoine] So yeah, that's it. (muffled by audio static) (static continues) - [Simon] We don't here
you anymore just... - [Host] Ah, you also had white noise? (muffled by audio static) - [Pierre-Antoine] You
don't hear me anymore? - [Host] I don't know who's- (indistinct) - [Host] Some white noise. (audio static) - [Pierre-Antoine] It is better? - [Host] Now it's better, yeah. - [Pierre-Antoine] Okay. So yeah, this is actually the complex part that processes the user
and concatenates the paths replacing the actual
properties by the next path. So when you access the
property, you get the next path and you can go deeper and deeper. - [Host] Hmm. So are you reconstruct
basically the path again for the auto completion on the other end. - [Pierre-Antoine] Yeah. - [Host] So you do two things. One thing is that you
calculate the return type but also in the input type,
you are typing that properly so that the auto
competition works basically. - [Host] Yeah, the
return type is calculated by this utility here, this whole path. It's quite simple. You give it an object that
can be any object actually. I just use the extends any
for documentation purposes, it's not actually used for running. And there's the interesting part. I don't know if you guys have
used recursive types so far. It's been released recently, but I've been using them for quite awhile in the ts-toolbelt. So the idea of path here
is that this type is going to call itself as long as
this condition is matched. So the idea here is to try to
go to the path that was given. So let's say type is 0 and
then we go path, then we can... (mumbling to himself) Very simple. And then you put name here. Okay. But what it's going to do actually, it's going to receive the
object, this one, name: string. And it's going to receive the path that's just going to name. So it's going to go through
this list basically. And that's the condition here, that's the stopping condition. As long as the pos<I> is
the friend of the length<P>, then it'll continue calling itself. I don't know if you guys
follow, maybe I should- - [Host] Okay. I think important to notice
that your extends function, line 18, is basically the true, when it returns true, it's the one, right? If it returns false, it's zero. - [Pierre-Antoine] Let's rewrite this so it looks more TypeScripty. (mumbling) Is the length of P. (mumbling to himself) So what is going to do is that the type is going to call itself, because when the position is
not equal to the length of P, then this part returns zero. And when this part returns
zero, it calls Path again. And when you call Path again, it calls this function
that goes to the type at that part of the Path, which
is in this case it's name. So we've got to At and
we get the type again and then it will do this again and again. So if you put that stupid type here, path, you will just be undefined. Okay, but this-
- [Host] This is smart. This is clever. - [Simon] So is this working
because if it's an array, so it returns... like it narrates because
it's an array on this type. Or is it just a special syntax for the... The array syntax online
like starting from line 18 to like 22 is like that... For me, it's like... - [Pierre-Antoine] This
array is a way for you to give the Path you want to access. Like, let's say if you At the... Let's put it again. - [Simon] No, I was just wondering like if it's a special
syntax for like the stop, the recursive, or... 'cause like for me on line 15,
in my head is like an object, it equals an object, an
area of objects, right? The path, basically, that's what it is - [Pierre-Antoine] No. Oh, okay. Yeah, maybe they should have... - [Host] Yeah, that's a map type. - [Simon] I guess that's the map type. Oh okay, that makes sense. - [Host] So, you can access
the property basically. - [Pierre-Antoine]
Actually, the path is just a normal object, is a regular object here. We pass it the O object,
that's this at the moment. Then we have the list of the path where we want to go to
which come things name. And this is a tool of mine that
helps to work with numbers. Like in TypeScript with you
can't really do iteration. There's no full loops
and all these things. So it's a way to have a counter basically. So what we do first is
we create an object. That's the false... We're going to create a false condition that we're going to just call
zero because zero os false. And when it's false, it will
just call the path again. And with At, it will go to
that position of the iterator, which is when you start,
it's going to be zero, so it's going to access name. So it's going to go At O,
and then in here instead when you're just beginning, it's going to do name
basically automatically. And if it's true, it
means that we are done and we can just return the type. And when you At this is going
to cause the type to unfold until the condition is matched. - [Simon] Yeah. Okay, now I understand. Yeah, I didn't think it has a map, but now I see it with a map. Cool. - [Pierre-Antoine] Okay,
so yeah, that's it. But, it's a lot of other types that I could go through, the split types. I don't know if you guys
have seen the split types. - [Host] Maybe you want
to repeat that again. So last time, we had as templates, type templates strings are still new, last time, we had someone
from Spotify showing us all the crazy stuff you can do with that. And split was one of them. Maybe you want to just revisited how the split is implemented. - [Pierre-Antoine] Okay, so
this one is a bit different. It doesn't involve
iteration on both indexes. Swift has a new feature that
is called template literals. And the amazing thing that they did is that you can use infer inside
those template literals. So if you have a type... Let's do a test again. (mumbling to himself) And then you can do extends, infer... You can say here it's going
to be actually a number. And here you're going to say infer. Infer A and here, it's a number again. And by doing this, you can... Let's root from A and let's root to never. By doing this, you can just... Oops. Oh no. What mistake did I do? Number. (indistinct) (mumbling indistinctly) - [Host] Syntax with a number,
I haven't seen that yet. I didn't even know- - [Simon] Yeah, just try like 0- - [Pierre-Antoine] Oh yeah, I'm sorry, I'm still in the TypeScript
4.1, but it might be why. - [Host] So old. (Pierre-Antoine chuckles) - [Pierre-Antoine] Let's see. That should have done it. Nope. - [Host] Yeah. Otherwise the zero is also. - That's strange because I
did this just the other day. Okay. Whatever. Anyway, but you can infer
parts of those strings. So you put your delimiter here. And this case it can be
any kind of delimiter. Previously, it was dot delimiter. And you can say, I infer the
first part of this string and then there's going to be a delimiter and then infer the rest of the string. I've called it before
string, BS, and after string. And then you just continue doing that. So you get the first path of the string to get the delimiter
that you want to remove and the rest of the string,
and you continue doing that until those strings empty,
and that way you can... you pile all these little
pieces of string into an array and you'll get the split string. So if you put the type out. I can do... Maybe let's see and
your delimiter is this. Then you get the split string. - [Host] Not bad. Can you elaborate why you have
this wrapper type in line 16? Why you don't directly call
the underscore split one? - [Pierre-Antoine] Why
did I split it like this? - [Host] Yeah. Mhm. - [Pierre-Antoine] Well, it's just for... make it easier to read. The TypeScripting has
a more elaborate one, but I felt like I wanted
to split the steps and I don't want to
repeat this condition at every iteration of the recursive type because it (indistinct). - [Host] Okay, nice. Yeah. This is some advanced
stuff that's fairly new, right? Yeah, exactly. Natalia just wrote. This is an open mic, so if
anyone asks questions now to the advanced types, now
would be a good time to ask. Okay. You still can ask later if you remember or if something comes to your mind. - [Natalia] I think that was so advanced people are scared maybe. (chuckles) - [Host] That could also be. That could also be. I think even the TypeScript
compiler is a bit afraid of Pierre-Antoine. (all chuckle) - [Pierre-Antoine] The
TypeScript team is afraid of me. (all laugh) - Yeah, no, this was really awesome. And again, I highly encourage
you to check out ts-toolbelt and also an important point
here again is that what Pierre-Antoine is showing
here is really advanced stuff, and you don't necessarily need to do that in your all day life when you build an
application with TypeScript. For the people who are
not using TypeScript yet, you don't need to know this stuff. - It's a the real waste of time sometimes. - [Host] Oh, I mean, you are
basically on the absolute edge and it's research, right? And we need to differentiate
between library code and application code. And I think library authors
are rather interested in the stuff if you want to give people a really, really nice
API, then you can hide... That's the beauty of TypeScript, you can hide this
complexity in the library and you can have nice
readable application code with all the auto-completion, et cetera. But usually, at least in my applications, I don't do a lot of events TypeScript. It's fairly simple mostly. Just FYI that people are
not scared of this now. This is rather a library code usually. Okay, so then we have another little presentation prepared from Emil. - Hello. - Hey. - [Emil] Your screen... So it's not as advanced, but I hope it will be
informative to some people. - [Host] It doesn't have to be. - [Emil] So basically,
I discovered the type of and the return type and
I just ran with it a bit for GraphQL applications. So the first thing I use
it for is data loaders, which, basically, like say here, I created a simple to-do application. So you have users which
has a couple of to-do lists and each to-do list has
a couple of to-do items. So usually you want to have
data loaders for each of them. So let's say you have like
a data loader for users. So that's my typical data loader usage. So with Prisma, basically, you just... (mumbling to himself) So you find many, you just
dispatch that into a dictionary and then map the keys again. Pretty classical data loaders and my issue was I wanted
some kind of object in my context that it
would be automatically type for an end results all
the data loaders directly. So I created this loader thing which basically generates
all the data loaders for all my modules with... We're going to see like what
the function actually does and then uses the type of data loader which is a function by
itself, this function, and then the return type of that function. So you just extract basically
the implicitly-generated return type of the function loader. And you can see here that
it's fully, automatically typed for you and you
don't need to do anything, which is an interesting use here. So if we go into like the user, it basically just calls this function that creates the data
loader and then assigns that to an object with potentially
other data loaders. So what is interesting,
like here it's a simple idea where you just load your user by the ID, but if you have a to-do list where you can either load
the to-do list via its ID or via a parent, a foreign key, so the user foreign key for example, Now you have like the main data loader and then you have like
by user data loaders. And then you can use... Usually, it's a good idea
to prime your objects, so if you have main
primary key data loader, then you pass it to your
children data loaders, and then you in inside of it,
you call prime on the primary. So like they're already loaded
in memory if you need to. So that way you can... Like how you're going to
use it in your resolvers is for example, here find list, here it's already done. But if we type it again, with loader, and then you have all your loaders. And then if I just want
to use the list one, ID.load and I just enter an ID. But if I want to do it by user agent, I created this by user, so if you just go by
user, then you download. And then you put that here. So it's an interesting like idea to like automatically type... So we didn't have to write
a lot of code basically, and then we have
automatically-typed data loaders on every point. - [Host] Can you, again, repeat or show again the context type? - [Emil] Yeah. Yeah. Sorry, I was going too fast. So here is just to create the loaders. So basically goes and calls each functions in each modules that is the
loaders function in the index, it's a default exported function. And that each of them,
they create an object which contains the data loader. If people are not really
familiar with data loader, it basically batches request together. So if you do like load... The problem with GraphQL
is like you can really, the end plus one problem, which is basically if you have a user and they have a multiple lists, you just want to do one
query for all those lists of like to-do list. And then each to-do
list has the most items, you want to do one query
for all those to-do items. So that way you like data loader them into one query to the database. So like, if we go into
here, basically, you see it like user, it goes fetch one user and then fetch all the to-do list. And then all the to-do items in one go, instead of doing multiple
queries to the database. - [Host] Exactly, I
think this in statement. Maybe you can keep the super query, I think this In statement
is important, right? Because it says user.id in, and then you can provide
a list off IDs, right? And then the next level... can you show the graph query actually that you're sending there.
- [Emil] Yeah, yeah. What I'm sending is this query here. So you go user and then you go list, I should have started with that, you go list and then you go items and then you can even go back to list because I created a back relationship. And since I primed it
with the main data loader, it doesn't do another query here. It just uses the already loaded
list since I primed it with- - [Host] Perfect. Because it has a per
request caching, right. - [Emil] Yes, it's per
request caching, basically- - [Host] If in the same request, you access the same thing again, then it sees, "I got
that already, so done." - [Emil] But if you don't prime it in the primary key data loaders, then you're not going to see that. You're going to have another request here to the database, because like you load it through the user at first here, like you load through the user to get the all the lists of their user, but here you're using its primary key. So if you don't prime your main loaders, it's going to do another
query to the database. - [Host] So, now back to the SQL queries that are generated based on- - [Emil] Yes, so that
generates that query. - [Host] Yeah. To be honest, we are
not in advanced meet up but still, it's- - [Emil] No, no. I mostly wanting to just
show like how you can use the type of a function, and then the return type of
that type to extract a type. And then I have... Like that was my base example
and then for another project- - [Host] Before you move on, can you, again, show the
context type, actually, the one that you have been importing? - [Emil] Yeah, so basically
what it does here is loading... That's your context object
that you pass around. I really like this pattern,
even if you're using rest to have a general context
is really interesting. And then you have like
all the loaders in there. - [Host] And that means
when you are now calling those loader function, line 15, you are basically creating
a fresh loader context, so to say, a fresh loader
instance so that it caches within that context, and we
just hope that your JavaScript a garbage collector just collect that away once the request is done- - [Emil] We do hope that, but like data loaders are always
scope to a request anyway. It's never a good idea. Well, in some cases it's
a good idea to share with- - [Host] I think if you
don't do the caching, but the batching part, you don't need to put it
on a per request basis. - [Emil] Obviously. Yeah, yeah. - [Host] All right? But in your example that you just showed, it's useful to have it
with the caching together- - [Emil] And, yeah, mostly
what I wanted to show is how you're going to use it in
your application which is... Because I don't like to type types if they can automatically
be generated for me. - [Host] It's interesting
because what you have here in this file that you
have open here right now, find list items, the next level would be that we automatically
typing the function, right? Like, because-
- [Emil] That's coming. - [Host] In next JS, that is
something I would like to see because then you could have,
like, they get static props get server-side props automatically type. It's not that possible, but
there is an issue open for that where people will want a whole module to be able to be typed from outside or the exports at least off the models. - [Emil] We have something like that. That's my next part. - [Host] Okay.
- [Emil] Let's close that. So, that was like the simple part. And then for another project, we wanted to have all the
functions automatically typed. So say you have like models and then... It's a different architecture,
but basically the same thing. So I redid that into a Nexus application, which is the same thing,
but like it's called first instead of being scheme offers
for people that use that. But the important part is
like say I have my models and I have to-do items,
so here, what I do is instead of having a context, I have this, what I call applications. And then that's a
function of my application of my model to-do items, so find many... We don't really care what it does, but like basically touches the data. And what is interesting
is each of those models, they have this index,
which injects a context. So we're using here, like
if people want to check it, we're using contravariance. So context, if I go into context, context is my base context. It contains like Prisma or
Stripe or whatever you want. And then application is
a children, if you want, of context and it contains
the return type of models, which itself is typed with all my models and all my functions automatically. - [Host] Mhh, it's using
the return type type-off you don't have to write
off this all the time. - [Emil] And there's
the interesting thing is you don't have app here. Like if we go back to
find many and you have you have have an application here, and then when you have it
inside the application, you don't have it anymore. Like it's automatically type... like it will be passed
implicitly down there. - [Host] What does application here? What is an application for you? - [Emil] Application is just another word for context, basically, but it contains... It's because you need to
pass contexts to application, otherwise, you have
recursive typing, right? Application needs applications to be. - [Host] Okay, but this
function create application in line 20, where is that called? - [Emil] That's like the
create context you have and- - [Host] Ah, okay, okay. - [Emil] I just call
it create application. - [Host] Ah, okay. - [Emil] And the trick is it only works if you disable one flag in your TS config, which is the strict function
types, which prevents you from using contravariance
in types, basically. - [Host] So do I understand
correct that is basically a little bit like a
recursive condition here. - [Emil] Yeah, it is a
recursive definition because basically you need... Well, contravariance works
that if you have a derived type and a base type, you
can pass the base type to the derive type and then it will work. That's contravariance basically. - [Host] Yeah. Yeah. - [Emil] So, here we're passing context, which is the parent to
application, which is the children, and it still doesn't complain
because we disabled that flag. So that's basically what our
inject function does here. It takes the context and
infers it as an application. You can also just do as
application if you want, they don't care about the typing, but I do care about the typing. So if I pass garbage in here,
it will not work basically. And then what is interesting
is when you go into... into here, then you
have, like you see here, the whole application is available. So you have like models
and then you can go here and then you have like all your models, user, and then we need to find one. And you call it with some inputs - [Host] Out of curiosity, how do you tell Nexus
to type this properly? How does Nexus type the app now properly? - [Emil] Oh, the Nexus thing is just while it's using the
application, so, right? It's importing the application directly- - [Host] Ah, so really say the
type is called application, and Nexus basically does
static type analysis. I didn't know that Nexus is that advance. Nice. - [Emil] Yeah, so
basically what it does... Like that's Nexus' stuff, so I don't want to really get into it but like they type your
context... (mumbles to himself) - [Host] So they then import your type and that way everything is time
safe on the context object. That's pretty awesome. - [Emil] Yeah, they import that here. - [Host] If you wouldn't have this, it's so much manual typing - [Emil] So yeah, it's a way to like... it's not perfect, but
it's a way to like... And then here, if you need... Like usually you would need... Like, for example, if
you have a subscription and you want to like call Stripe, then you would do like app.service.stripe which is a in services here. So you can call from other parts. Like if you have an app object, then you can call all the other methods that are in that app
object that automatically. - [Host] So you basically also have a dependency injection with this, right? - [Emil] It's a kind of
a dependency injection- - [Host] Which is nice. It's a very strong pattern
and SJS for example, right, They have the services, and then you can inject what
you need and the same here. That's awesome. So that means you can also, obviously, easily mock or stop things, right? If you want to do-
- [Emil] Yeah, yeah. You can also do the same thing... Well, similar thing with
classes, if you want, basically. Like you can have like a to-do item class, and then you have like inside
of the to-do item class, you have like defined mini function. This is just like a
functional-ish way to build it, or like building objects that are... 'Cause like here you
never specify, basically, the return type it's always
inferred from the injection. And the inject function is
just like taking two types and then basically the function
needs to respect the app, it needs to like have
this app context in it as some kind of input and the
return some something else, it will automatically like populate DNU with the function you pass and that's how it works basically. - [Host] Can you, again, show how you call the inject function? - [Emil] Yeah. Just here, so that's the method. - [Host] Ah, okay. That's the actual function implementation - [Emil] Yeah, it's the actually
function implementation. So it does create a lot of
objects on every request, but it's not that bad, like... - [Host] Okay. Okay. So I guess if people wouldn't use Nexus, they could use this pattern also with other graph (indistinct)
frameworks, I guess, Type GraphQL here and whatnot, they would also allow you
to type the context once without having to re type it all the time. - [Emil] It was just like... What I really found is that the type of and then the return type
is a really powerful thing that you can do a lot of stuff with. There's like a return... That type of a function and the return type of that
type of is pretty interesting. - [Host] Can you, again, show
for example, in the models, what you're importing from the models? - [Emil] Yeah, so the model is like... Oh, I didn't show that. But, it's basically just an object which call each of those model. Basically, call the inject
function of all of those model and just creates an object of that. - [Host] Can you show an example how would they implement it? Are those then resolvers
for the whole type or how is that working? - [Emil] That's like the to-do like... We were in to-do lists, so
that's where you inject- - [Host] Ah, that one, okay. - [Emil] That's the inject part, and then you just combine
all of those injections into one bigger object
just for simplicity. So if you need to add a new model, you just add it here and
then you go into your index and just add here and
automatically everybody can use it. Like you don't need to
import anything else in your application anywhere basically. You never do basically import any more. You just go into... You just use a app and then you do stuff with app. Like all your functions
are in app, basically. It's interesting different model. - [Host] Now, a stupid question. Why wouldn't you directly use,
put like Prisma on the app? Why do you call through the models? - [Emil] Prisma, I use on the app. - [Host] Ah okay. - [Emil] It's just like another like... because if you need to do
fancy stuff like subscriptions, it's not a database type for example. So I just need to have this, create one. - [Host] Ah that makes sense-
- [Emil] Business logic- - [Host] If you have business logic that you need to attach them- - [Emil] And usually I prefer also to wrap all my database call even
if they are to-do item, I could just do prisma.todoitem.findOne, but I prefer to wrap it in two methods because then I can do access control, or I can do stuff like that that is not... So for me, like if you're
using the Prisma directly, it's not a really good... like I never used the Prisma
directly in my resolvers, I just always pass
through the models system, and then, yeah. - [Host] But I think
that's a good pattern. That's what I also meant with nest. I think they use that a lot that they would never expose
Prisma directly to a controller but they would always do
it via models or services. And that way you have the control that if you want to reuse this
logic and different resolvers maybe in your GraphQL server,
then you don't need to, for example, rewrite this line 32, where you do something on Stripe. It's just always the
same thing, basically. So you can reuse that logic no
matter from where you use it. That's smart. - [Emil] Yeah, so those
are like full examples and they are working TypeScript servers. Both of them, they are like
called first and a schema first. So I put them on, on GitHub
if people wanna check them out and see like the why the
typing works and all that. - [Host] 'Cause I think you have a nice architecture there in your code. I really liked that, how
you structured everything- - [Emil] Yeah, it's
two different projects. Like it's not really TypeScript related but like it's a bit TypeScript related, it's like either you
put it using resolvers or you just go in modules, it's
two different architectures. - [Host] It's, let's say,
TypeScript in practice. - [Emil] Yeah. So that's about what I have. - [Host] So someone just
wrote, Sebastian DuBois, "I'm really interested in
having a look at the code. Where will you publish it?" So yeah. Exactly. I would also like to have a look at that. Yeah, because after all,
what we saw today, I hope, is that TypeScript basically
allows everything... Ah, yeah. That's it. Exactly, if you can post that
in Zoom, that would be nice. TypeScript` basically
already today allows you to do kind of everything, but it's on us what do we do with TypeScript, right? How we structure our projects, what crazy types were building... Someone asked earlier
regarding Pierre-Antoine, "I would be more interested in use cases." Pierre-Antoine, if you're still there, maybe you want to also elaborate on that. So use cases, I think there were talking about the split time or
at least more advanced like typed string, literal types. So I think it's still experimental. Like I can also speak to that question, it's still all experimental
and you probably don't want to use them yet today in production. But people are already, for example, building a GraphQL paza or JSon paza, with a the string literal. So you could really write down
your GraphQL string as it is, and the return type is
automatically typed. These kinds of things you
could now build with that. It's still all, not so
performant, because you're a bit, let's say abusing the type
system there to write a paza, you can write a paza
much more efficiently. We were joking last time
I think Simon mentioned that he can imagine like in a few years the computer science paza classes where young students have to
write a C paza in TypeScript. That that could have happened, who knows? It's still all early and I'm
not really aware of people yet using that in production,
but it's coming, it's coming. At Prisma, we use it a little bit already, the string literals, by the
way, for error handling. So I'm in our... So for the people who don't know, Prisma builds tools around databases and our main tool is called Prisma Client allows you to access data and it's mainly a TypeScript client. Also it's available in Go. And what you can do, you can... we have a groupBy feature
that we recently introduced and it turned out the types for a groupBy are kind of complex and we had conditions within the argument itself
that we wanted to express. And we were, for example, saying that if you want to
orderBy a certain field let's say that field is called... That field is called name. Then you also have to, if you
want to orderBy that field, then you also have groupBy that field. So we needed to be able to
express, on a type system level, to say, it's not in the groupBy statement. If it's an orderBy, it
also has to be in groupBy and to pick out this name of the key, for that we used the new string literals. I can also share a link about that. I didn't prepare a demo for that now, but we already ship it in
the latest Prisma Client you need with that
TypeScript for that one. Good. I think if the heads haven't been smoking yet after Pierre-Antoine's demo, then I think they should smoke now in a good sense. Is there any question? Is there anything anyone wants to ask as we still have everyone here? If not, that's also fine. There's one last little
announcement I want to make. I just posted the link here in Zoom which is a link to a Udemy course. Someone created an end-to-end
React with Prisma course on Udemy and if you use this
link, you get it 100% for free. So it's basically showing you Emil... I would say Emil is
already an advanced user. He himself is able to like... He sees all the blocks and knows how to put everything together, but sometimes it's useful to just see from beginning to end how
everything fits together, and that's really where
this course helps with if you want to use Prisma and React, then this course is the
right thing for you. One last link I would like
to share about that is only for Prisma users or anyone
who wants to try it out. We are right now in preview
for native types at Prisma. So native types basically
allow you to access things like bites or buffers from your database with a Prisma Client, instead
of just, let's say an end. Native types, we'll go into GA next week. And if anyone still
wants to share feedback, now is the right time, now
is the last chance basically to do significant changes. Next week, Tuesday, we will
already go in GA with that. Awesome. So then, thanks everyone for coming. It was, again, a pleasure to learn. My head was for sure bent. I was challenged with some of
the stuff that we saw today. I hope some of you could learn
something today and yeah. And again, thanks a lot to
Natalia for organizing this. - [Natalia] Thank you. - [Host] Awesome. So then I think that's
it have a nice evening and have a time no matter where you are, in which time zone you are on this planet. (Natalia chuckles) - Classic. - Yeah.
- [Natalia] Okay, bye-bye. - Bye. (soft music)