TypeScript Utilities Every Developer Should Know About
Introduction
Meligy introduces himself and his background in web development, emphasizing the widespread adoption of TypeScript across various domains.
Regions and Route Configuration
Meligy advises against using regions in code and demonstrates configuring routes with type safety using the "Record" type in TypeScript.
Basic String Types and Utility Functions
Discussion on basic string types in TypeScript, including string literal types and utility functions like "lowercase," "uppercase," and "capitalize."
Creating a Custom Fetch Wrapper
Meligy explains how to build a custom "fetch" wrapper that allows cancellation, leveraging the "Parameters" and "ReturnType" utility types.
Dependency Injection and Constructor Parameters
Explanation of how to type-safely handle dependency injection with classes, using "ConstructorParameters" and "InstanceType" utility types.
Understanding "this" Parameter Type
Exploration of the "ThisParameterType" utility type for inferring the type of the "this" parameter in different function contexts.
Building a Blog: Models and Data Manipulation
Defining data models for a blog (author, post) and employing "Omit," "Partial," and "Required" to handle different data states.
Readonly Type and Its Limitations
Using the "Readonly" type for immutability and highlighting its limitation in enforcing deep readonly behavior.
Extracting Types and Building Reusable Components
Demonstrating "ReturnType," "Awaited," and "NonNullable" to extract types from asynchronous functions, enabling reusable components.
Combining Types with "Extract" and "Exclude"
Explanation of "Extract" and "Exclude" for combining and filtering types to create a common type for similar components.
Conclusion and Q&A
Meligy concludes the talk, quizzes the audience on their knowledge of the presented utility types, and encourages further engagement and discussion.
Looks like, we have a lot of people who like TypeScript.
That's awesome.
But I just also want to make sure how many people already use TypeScript.
Okay, good.
We're in a good place.
Good place.
I'm not sure I know, many of you.
So, as Sharky said, I started, development professionally in 2005.
And when I started with, Microsoft ASP.NET.
The thing then was you don't need JavaScript to do coding.
And Microsoft had this, the old people here would remember.
Web Forms and Update Panels and SPA's were called Ajax then, and you would avoid JavaScript because you can, and then turns out you can't.
So, jQuery was there, and then Knockout, and then AngularJS, and then Angular.
I kept blogging about this stuff and somehow made a name for it.
So I made a user group, called NGsydney.
Only things that happened is that I had a consulting gig with React and I found myself actually moving to the other end, which made it really weird to be the guy who runs the Angular user group.
So I left that to the, Google, developers group now.
And then I noticed, oh, actually With Angular, with React, with whatever, now everybody's just using TypeScript.
So though I'm now like a managing consultant, I do a mix of back end and front end.
But whatever you're building, you might be doing something on Azure with Service Bus and talking to multiple databases and doing this whole architecture stuff.
But it's pretty much always, there is a website or maybe sometimes a mobile app.
And, there's an API, no matter how complex it is, and most likely you're going to use TypeScript, at least on the website, and maybe also on the ABI.
API Just this year I was actually, reviewing some APIs that were created in Koa, that's like in NodeJS, and of course they were written in TypeScript.
I got to do some work with Apollo Server as well, and that was also TypeScript.
So, it's everywhere, and it just makes sense to, be familiar with it.
And that was pretty cool for me because my, my start with, Stack Overflow a long time ago.
I, this number isn't great because yesterday you heard somebody who had 100 cases.
That's not, as much.
But my, my number one, answer that I was a bit ashamed to, to show in here was, how do you handle hover over selecting jQuery?
Is that still getting me?
So what we're going to talk about today with TypeScript is some utilities that I find people implement, quite often.
And when they see me use it, they're like, oh, that thing exists.
That's pretty cool.
And there are 21 of them.
I'll try to, cover as many as I can very quickly.
But I want you to also keep your own numbers, your own scores.
So, hopefully you know as many of them, but at the end I'll come and ask you, how many did you find that you already knew, and how many, that you didn't?
With that, I think you might be, tired of so many slides, so let's switch to code.
Now, what we're trying to do is we're trying to make a blog website.
We will pretend like we are, because we're not really creative, and we'll see just different, situations that show how to use different, utility types in there.
First thing I'm going to tell you to do is don't use regions.
I'm just doing this for the presentation, and I can see now that you don't actually get to see that.
So let me switch it again very quickly.
I think it's because the presentation is still playing.
Stop that.
And then we do this fun Zenmode again.
And now you can see.
And now you do realize what I was talking about, which is I was actually using, people who do dot NET know this thing because it used to be in the main Visual Studio, but just don't use it, it's just for presentation.
So let's start with, ones that hopefully a lot of you know.
So here I created, some React components, or like pages if you may.
I have.
Home, About, and then, I have, the blog posts, directory, or listing, and a contact page.
And then for each one of them, I want to have, routes.
So my route will typically contain of a title, a path, and a component.
Nothing special.
So, I want to create, my route, so I just put, Home, about, blog, I didn't even, include this one in here, and what I did is I just, put the title, put the path, the component, nothing really, special in here, except I want to be able to get an error if I missed something, and you can see that I do get an error that, we need component that's missing.
That, that works, and that works because I have this here, the type record.
And the type record essentially is like a dictionary.
You have a key, it's an object.
All the keys have to be of a specific type, and all the values have to also be of a specific type.
And you would have noticed, and I have a few extras, bonuses like this from, time to time.
I do satisfies here.
I could have done this, as, and it would work, but I'll just get my, menu config, it will be just this record type.
The reason I did satisfies is satisfies gives me the same effect as, casting.
Except, I still get my object exactly as I made it.
So I get the benefit of, getting an intellisense for the generic type, but also having the result type to be the specific one, which is pretty cool, and that's not I tell you that, but also a few people don't know it.
Now, the interesting thing about this in here is, as I said, If I try to include this, and then there's something missing, you would see I get this error.
And by the way, when you are expecting error, if you're doing any tests or anything like that's one more extra.
You can do this TS expect error, but unless you're like writing tests for types, again, don't use this.
Cool.
So, a few things that I, or a few types that I wanted to just get out of the way because they're so obvious.
When, you, if you have been using, TypeScript for a while, you already know that strings can be their own types, like the exact string.
And you can have, types that's like ID, hyphen, a number.
And then, it has to be like that, et cetera.
So, what I'm doing in here, I'm saying, okay, these are like my route keys.
I want to get them.
The way I do it is I just say key off, but because my menu is just an object, I need to also add typeof in front of that.
That's cool.
So I get route keys.
Here you go.
It's either this or this.
And then, for example, let's say each route key actually has a server endpoint in front of it that has the same key, but it's like a lowercase because it's a URL or whatever.
So what you could do is you can define types this way, you can mix different pieces of strings, and that's TypeScript itself, not the utilities.
But the utility for us is, I can do lowercase like that, and this will give me, you'll see, sorry, that was, this will give me API slash home, slash about, slash blog, slash contact.
Although, this were capitalized, and capitalizing would be a bit interesting.
Same thing with, uppercase, nothing really to mention here, I just tried, I said imagine that I have a health endpoint that has a key as capital, just really making up a justification for it, but let's say this health endpoint returns either success, warning, or error written like that.
Silly, silly API, that's fine.
But maybe I have a UI thing for it and I don't want my UI thing to be as silly as that.
So I can say, capitalize this.
And It almost works, so you will see success and warning here, they're all lowercase, so it did it the right thing, but it didn't do anything to error, it left error all uppercase, and that's because capitalized and uncapitalized are very silly, they just handle the first character.
I don't think you'll be using this particular tool, but to show you how, why it's really funny, when you see error in here, that's just, yeah.
Cool!
so, we're just opening a bit.
Let's go to something a bit more Simple.
So, if you know about, the fetch method, how get data from the server, fetch has one small issue, which is you cannot cancel it.
To cancel fetch, you have to create this thing called a port controller, pass it to fetch as a thing called signal, and then this port controller you can call a port on it.
So I don't want to have to do all of this any time I want to cancel, so I created my own fetch wrapper.
Now, fetch wrapper, I want it to take whatever parameters Fetch takes, and return whatever parameters, sorry, and return whatever the return of fetch is.
I don't care what these are, because I'm just driving it.
And turns out you can simply say parameters, and then it gives a type of any function you want, and this will give you the parameters in here.
So, I see it gives me the URL, it gives me the init values.
And same thing you can also do with, a return.
And this way I was able to make my fetch wrapper, which just returns, the result of fetch, but also the cancel method in here, which is this.
This one.
So, it's very easy, and I hope you're counting, by the way, how many have we got so far.
Okay, nobody's counting.
I'll be testing you at the end.
All right.
Excuse me.
Now, this is the most complex one.
I was going to end with it, and then I was like, I want to end on a high, So, let's say on the server, we have a proxy, so we call another external API, and we have our own client for that, more than just a feature, like we have a proper client, and it takes the base path, and timeout in seconds.
Now, if I want to create a new instance of this, it's very easy.
You'll see.
New, this class, and then I pass the constructor parameters, right?
Fairly easy.
But let's say, on my server, I want to have dependency injections.
So I have an IOC container or, something that allows me to share this, one client for every request, even if I'm making multiple, proxy calls ot to APIs, I want to use this one.
So I did my poor, doesn't really work dependency injection, and the idea is, I want to be able to say, hey, my container, register by request, and then I'll say, okay, this one, the externally by class, with these parameters.
Now, why am I doing something like that?
Why am I passing it like as a generic thing?
Can anybody guess?
Because in TypeScript, types are just compile time.
They're not run time.
So if I do it like that, there's no way that my run time will know about this external API so that it's able to, create an instance of it in runtime.
And that's why I have to do it like this.
But if I do it like this, what's even the type?
The class itself, is it of type class?
What really happens in here?
Turns out, now, it goes a bit .Like a constructor.
So, there is an implicit, generic argument.
You don't see it because it's, inferred from the call.
And, when you see, all we did, we said, okay, new, something, and then we give it parameters.
And that's exactly what we're saying it is.
We want a constructor, a class, which is simply new, something, that returns something, it returns here the external API.
So once I got this, I can get that class as a constructor, so I can say, in here, so essentially, what I'm doing is, I have a, a dictionary.
That has the keys, and it calls the functions to make the objects.
So in here, I just said, okay, I'll use the key as just a constructor name, which in here would be the class name.
But when it gets the object, just call new with the arguments.
I didn't need to know what the arguments are.
I didn't need to know what the class is.
And I have only one problem.
I need these args to be typed.
I need to be able to, in here, if I pass this as a string, I need it to complain.
So how would I make it know, that, it needs to pass this?
Simply, and you would have seen it already, there is a built in type.
If you manage to get the constructor, which is again, new something, return something else.
If you manage to get , to capture that, you can just say constructor parameters, and it will get you, what you want.
Now, I'm able to say, okay, what is the return of that?
Like, how, I can't even say, If I'm using it like that, to get what I registered, how would I know what's the return type?
Again, it's a class.
So for this, it's very simple.
We have, just like we have the constructor parameters, we have the instance type.
Now, the difficult ones, but also hopefully the ones you will not really use, they are more about this.
So, what I did in here, I defined this, instead of, I could have made it a class with static parameters, I decided to make it an object just to show, some values.
So when I defined this, I defined it like that.
And this really works just your normal lambda thing that captures this.
And that's why I'm able here to say this.
Without doing anything special.
However, in the get, I decided, just to make a point, to make it with the old function word.
Those who are old enough will remember that this function means you can actually modify this parameter.
You can make it anything else.
You can bind to other things.
And the way you tell TypeScript that it actually TypeScript wouldn't even know, does this even need this or not?
But to do this, like to tell TypeScript that you expect to be using this parameter in a normal function, it has to be the very first argument to a function, and it has to be called this.
Again, TypeScript not, not utility types, but now I can figure out what the type of this and get by just calling this parameter type, very obvious name.
Okay, what about the normal one?
What about the register one?
Can I also know what is the this of this function?
Here, it was a parameter, here it was not.
But, even, let's say I am doing, forget even, I'm doing bind.
So I'm losing the, this, because now, like, when you do bind, if, again, for those who are old enough, you don't get, the this.
That's, you can just call it normally now.
That's how we used to do things in, React long, long time ago.
And the way this works is simple.
Bind.
This is the definition of bind in TypeScript itself.
It simply says it has a type that says omit this parameter.
It's very obvious.
Now, with the silly stuff out of the way, let's start building.
So, our models.
We have an author, we have a blog post.
And the blog post would have an ID, title, long body.
And then author, publish date, and slug, which is the URL.
Now, if I'm creating a new draft, it probably wouldn't have an ID, because it's not saved in the database yet.
So, what we do, we just say, omit the ID.
And we can also omit other things in here, just the same way.
Oops, not sure why it's not working here, but I'll show it to you in another one.
This allows me to say, okay, I want to be able to create a post and without supplying the ID because you will create an ID when you go to the database.
Then we can also say, okay, I want to update.
So if I want to update, I'll need the post ID.
I always I would never write post ID like a string.
I'll always try to infer it from my model.
And that's not, utility types.
But.
Once I have this draft, I can also use it in here, but when you are updating, you don't have to provide all the values.
You can provide a few of the values.
And that's what the partial type does, and I'm hoping a lot of you already use partial and know it.
Might be one of the most popular ones.
Do you know how it's implemented?
It's very simple.
It's, you say partial of an object, it gives you another object.
And for each key in this one, it just makes it optional.
And the value would be the same value for this, key, like the blog post in this case, of the property.
So now they're all optional, just like this.
And we can see this.
Now, what I want to do, I want a published post.
So a published post is the opposite of, of optional, sorry, of partial, it has to have all of them.
So I make it required.
Required is implemented the same way, but what people don't, many people don't know, is that there is actually an opposite of the question marks that make things optional.
Which is dash question marks.
Which is pretty cool.
And now, I want to do a few more things.
So now I have my blog post.
At the end of the blog post, I want a list of related posts.
This will be just links.
So because it will just be, links, so I'll just include the ID, the title, maybe the author, and the URL, so that I can link to it.
Now, to do this, I want to be able to get the post itself first.
So, I have a method, it says viewPost.
It goes to the database and fetches the post by post ID, but this is view post, so we shouldn't be able to edit the post, right?
And that's why we have simply read only.
How do you think read only is implemented?
I love being able to do this, by the way.
Just CTRL click or CMD click and you see it right in there.
The only things you wouldn't see, by the way, are the string types.
They're like native.
And it's just the same way you would implement the other ones, you just put read only in front of it.
Do you see any problem with this?
So, essentially, in here, I, if I do that, I get an error because I cannot modify it because it's read only.
That's good.
So I'm expecting an error here.
However, I can do this.
There is no expect error in here.
It accepted it just fine, because I'm modifying a child's property of author.
Nobody said that child's properties would have to be read only also.
It was, as you saw it, it was just one level.
You can, I think it can be a nice homework for you to go and see how to do deep read only [????] Very easy.
But I just wanted to, make you notice that this is a catch.
And remember, this is, all, just compile time.
If you really want safety, you want something like Zod, in the previous, talk, and that sort of thing.
So, now, let's say I have, a page per author.
It's very similar to related, related posts.
It will just have, like summaries of the posts and links to them.
However, this get author post, whoever wrote it, was certainly not me.
It's a bit silly.
Because they just put the properties like this.
I told you I would never do that.
I'd always try to infer things from the other types.
That's why we use utility types.
This one, we just did it like that.
So let's say, we're checking if the email, of the author or maybe the ID, whatever, is empty.
We return null.
Otherwise, we return an array of these properties.
ID, title, publish date, and slug.
And you will notice that we, there's no way for us to get this type and I want to say an author post, just like I said, the related post in here.
To do this, we can do some magic.
We already saw return type, right?
In fetch, if you remember.
Anybody still awake?
I know, after food.
However, if you do this, you will see, it tells you, you get a promise of either an array of this object or a null.
Because that's what we do.
We either return a null or an array, but we made it an async, and that's why it returns a promise.
So the first thing we need to do is, we need to unwrap this promise, and that's very easy.
Do awaited, awaited will take the promise out.
So, again, here, the promise of this, now, is just the array.
The array, or the null.
But I don't want the null.
I want, I want this object itself.
I don't even want the array.
So, there is a utility type called non nullable.
So, once I get the non nullable, then I get the array without the null.
And then once I have the array, and this is not utility types.
If I have an array of something, I know that array of 0, array of 1, array of whatever will give me the object right, so that's why I say it's an array of the number.
And surely, I finally get my author post.
So, silly API, I don't care.
Now, once I have this, or why would you actually do this?
Why would you try to extract types like that?
Because then you can have your clean, React or Angular view component that takes an author post as a property.
You only care about, suspense or fetching or whatever.
You prepare the data, you give it to the presentation component and it just renders it.
And this is how we get it.
Now, the last thing I wanted to, play with in here, and let me double check that I didn't is anything in here?
Okay.
We good.
So, I have, I want the related posts, and the author posts.
They're the same thing.
They're just lists of posts that are just links.
So can I make them one component, and instead of passing it a related post or an author post, I just give it one type?
Of course I can.
So, there's another type, and this type, it's very interesting, because I'm not using it for what it looks like it's used for, but it's very, dynamic.
So essentially, what I'm trying to do here, I'm saying, extract, which is intersect, the types, that I'm passing to you.
So here I'm passing an author post and related post.
And if you remember, a related post has author and slug, while this one has published date and slug.
That's the difference.
So when I say extract these two, I get, these are the keys that are in there.
And why does this work?
What does extract even do?
As usual, let's just have a look.
So extract simply takes two, two types, and then it says, if the first type extends the second one, return it.
Otherwise, never means don't return it.
So what does this mean?
Now, when, we say extract, we say, okay, here is ID.
Does ID exist in here?
Yes.
Does, title exist in here?
Yes.
Does publish date exist in here?
Yes.
Does slug exist in here?
Yes.
And that's how it would work.
So I get this list, but there is one thing that I'm not sure I really want.
I'm using slug.
Why would I need to expose, the blog post ID.
I don't want the ID.
So I can simply say exclude.
Exclude is the opposite of that.
So let's say, you know how we have, omit and pick.
So pick chooses the values, omit, removes them.
It's very similar in here.
Extract, give me the values, exclude, excludes them.
And the way it works is actually also very interesting.
It's the opposite.
Do you see?
So it says, okay, you have T and U.
If T extends U, we actually want to exclude it.
So, if T, ID, title, slug, so these are actually types.
It's either this type, either ID.
Or title.
Or slug.
So we do it for each one of them.
ID.
Yeah, ID matches.
Then never.
Then it will not come.
And then title.
Title doesn't match ID.
Slug doesn't match ID.
Then they're good.
And I think one of the, if you don't play with TypeScript types very often, You probably really underestimate the never, type.
It's not a utility type, it's like a built in thing, but it's really, cool.
And now, I only have keys.
So how would I map them to an object?
The ones that would be the props to my component.
It's very easy and you have seen it done in so many of them.
I just make a type of my own, where I say, for each key, in these ones, and you got my mistake.
Pretend you didn't see that.
And, for each one of them, just get, oh, sorry, it's actually published in here, because it's, these are just the keys, and then I need the object itself, so, if it is an ID, I want to see a string, if it is, a published date, I want to see a date.
Let's just call it like that.
And this, indeed, would give me the equivalent in here.
Now, that was surprisingly, still within the time, which allows me to embarrass you and ask you, okay, how many people So, already knew about five types of what, we've seen.
Very good.
How many got ten?
Like they already knew.
Okay.
I want you like really high because I can hardly see you.
Fifteen.
How many already recognized fifteen?
Oh, you would have to be counting to know this.
Okay.
How about twenty?
More than twenty.
Okay.
Now, the really good people who already knew more than twenty five, please stand up.
Okay, I couldn't trick you because there were just 21.
All right, I hope that wasn't exactly a waste of your time.
I always love to share links and just resources, usually from people.
Many of them are already speakers in here on what I still call Twitter.
I know.
And, I would, if you want to, also just chat, ask anything, just mention me on Twitter, send me an email, or talk to me on LinkedIn.
Thank you so much for engaging, and yeah, hopefully we find other interesting topics to talk about together.
Thank you.
Thanks, Meligy.
[Applause].