JASON: Hello, everyone. And welcome to another
episode of Learn with Jason. Today on the show we've got Matt Pocock. How are you doing? MATT: Doing well. JASON: Great. I feel you have rocketed up
in in the TypeScript space You have a course coming out with wizard illustrations.
It's special. But you're brand new to the teacher thing, not the teacher thing, the
JavaScript thing. Let's maybe have a little bit of a background for folks who are just
becoming familiar with who you are. MATT: Sounds good. So, my weird background
is that I was a singing and voice teacher for 6 years. Before I became a Dev. That's
kind of where -- I don't know it's where my heart is. But it's definitely where the bulk
of my experience is. I have been a teacher for a lot longer than I have been a Dev. I
think I started building applications for my students, building these sort of weird
voice apps that would tell you the quality of your voice and all this crazy stuff using
web audio APIs. That was the my first JavaScript app, diving deep into the web API, and doing
form transforms. And I realized I could probably get a job as a junior Dev.
So, I got a job as a junior Dev. Then my next job, mid-level Dev, next job, senior Dev.
And now I'm here. I'm working at Vercel. It's kind of mad, really. It's felt a bit like
a rocket ride. And I think a lot of that is just being able to blag my way into situations
that I have no business being in. And I think the accent helped as well. JASON: I think we have proven that you sound
20 IQ points smarter whenever you have a British accent. MATT: I think you sound more charismatic as
American. That's my take. JASON: Yeah. No, the thing that I've noticed,
there really is a musician to engineering pipeline. Because that -- I think it's a similar
problem solving space, right? Like you -- you're looking at patterns, you're looking at ways
to combine old patterns in new and novel constructions. And that's what makes things interesting and
exciting. And so, what I found, I've made this joke before. When I was a musician, when
the band broke up, I learned that the only thing about being a musician that I wasn't
good at was making music. All the other parts, the booking and like all these things that
were business skills, right? How to go on tour. And also the construction parts of music.
Like understanding patterns and all those other things, that all felt good to me. So,
when I moved into a Dev, oh, man, like 95% of these skills crossed over. It's really
interesting to see how that works. MATT: It feels in many languages that Dev
is a more limited language than music itself. Music feels a lot more broad in terms of the
genre and just the stuff you have available to you. It feels like you could go massively
into depth in terms of harmony and scales and stuff. And JavaScript, its API surface
is tiny in comparison to music itself. JASON: Yeah. And we're seeing like a couple
really good comments here. Music is the original programming language. That's true. Because
if you think about it, when you write sheet music, you are providing a set of instructions
to a future -- I mean, to a machine. The machine was hand-operated. Like a piano or a violin.
A set of machine instructions. That's fascinating. Math is the OG language. Yeah, you're probably
right. But really, this stuff is so fascinating to me because it really is -- it all just
comes down to patterns, right? How do you communicate somebody else what you wanted
-- what you intended? And so -- MATT: To me, the other thing too, the problem
solving element of teaching was something I just got addicted to. Especially with like
a singing teaching and voice teaching. Someone comes to you with like a specific problem.
Like I can't hit this note. And it's like a mechanical thing. You know? Often it's an
emotional thing too. Like you have a sense of the person feels like they can't get there.
And so, their body tenses up and all this sort of thing and you get this tension. It's
correcting that mechanically. So, interesting and there's so much in-depth knowledge out
there. And so, coming to Dev, it felt like, oh, this is -- we have this very complex system
that we're building. And it actually felt much easier than voice teaching, weirdly.
You can't componentize the voice. JASON: Sure. Yeah. That is actually something
that's interesting. Like you can't -- you can abstract certain things, right? You can
go out and get a drum machine and something and play loops. But it's not the same as when
you componentize code. There's some really interesting stuff you can do with abstractions
in code and all that. But that maybe is a slightly clumsy segue into what we're talking
about today, which is TypeScript. Which is a layer of abstraction on top of JavaScript
that adds typing. Right? And so, this has been the sort of thing for
a lot of JavaScript Devs, myself included, TypeScript is a source of a lot of anxiety.
It's just JavaScript, except it's definitely not and you get mystery errors. And there's
sort of a programming language itself within TypeScript. And so, I get certain things.
Like if I'm just gonna write a type and say, you know, I've got like a book and the book
needs an author and a title and a publication date and I can just say, okay. Well, you know,
the title is a string. The author is gonna be a string. Or maybe an author object if
we've got more details. That all makes sense to me.
But then you start getting into stuff that is absolute just so out of my comfort zone.
Like why does this say, like, angle brackets T? And then there's like, you know, this -- all
these other things that are happening. What is that? Why does that exist? That's kind
of what we want to dig into today. We've talked about the early layers of TypeScript on the
show before. We've had several great episodes. I'll make sure those show up into the show
notes for folks. Actually, you know what I'm gonna do? I'm just gonna link to the whole
TypeScript section on Learn with Jason. We're got all kinds of stuff in here. More episodes
than I thought. Six episodes that I dig into TypeScript on Learn with Jason. MATT: Nice. JASON: Most stayed away from the advanced
topics. We were building the foundational knowledge. If you are watching this today,
if you have never used TypeScript before, you want to watch the video on demand. What
are types and how you set up a TypeScript repo with the TS CLI and stuff like that?
So, Matt. Why would someone need this, like, really advanced TypeScript? MATT: Yeah. So, it's being interesting. Because
TypeScript is like -- it's just JavaScript. It's JavaScript except there's -- I kind of
like to think of it as it's kind of like you're speaking English. Except it's almost like
you have your English teacher watching over you and with squiggly red lines while you're
doing it. A lot of people react badly. This is terrifying. And they get good enough at
TypeScript that they can make the red lines go away. That's great. I've mastered TypeScript.
I know what my teacher is saying and I can understand that. That's really, really good
if you're writing applications and consuming types. Kind of from other libraries. If you
think you're building a Next.js app, for example. You might want type checking on get static
props, all that sort of thing. And those type definitions that you pull in from a library,
they have been crafted by someone. And a lot of that type code is really complicated and
it's complicated in a way that it's pulling out different bits of inference and pulling
things together. And TypeScript is an incredibly powerful language whether you get into the
really deep stuff. And what it allows you food, it allows you
to build incredibly flexible APIs that then application developers can consume. What it
means, you can be at the top, trickling down all this beautiful inference and knowledge
and power down to those below you. It means you can become an enabler to your team. You
can become the TypeScript wizard on your team. That can build these simple APIs that are
easy to use and consume. But that enable incredibly powerful type checking for free, basically. JASON: Yeah, yeah. And so if I kind of grossly
oversimplify what you just said, when we're talking about libraries, libraries are dealing
with a lot of unknown inputs. And so, TypeScript gives you that really advanced capability
to do the inference and everything you need so you're using typing as you're using the
code. That's a really good use case for advanced TypeScript. If you're writing a library yourself
and you know what the inputs are and you're not taking a bunch of arbitrary user line
code, you may not ever need this. Is that a fair assumption? MATT: Yeah, I think if you're consuming stuff
from -- I think the point really -- or the dividing line is, if you're writing an abstraction,
and virtually any abstraction, then you're probably gonna need some kind of -- even intermediate
to advanced TypeScript to make that abstraction feel powerful. Because in JavaScript you can
basically pull anything into a function, return anything out of a function, do crazy stuff
like all through it. And if like you're not using TypeScript to its fullest extent, you
probably end up with the user themselves having to annotate the code writing, this is this
shape, this is this shape. Where if you can get it to a point that it's inferred throughout
that, in any abstraction you write, then you're probably going to have a good time using advanced
TypeScript. JASON: Gotcha. MATT: If you're building a team, they're force
multipliers for the team. Doesn't matter if it's an application or -- if you're in charge
of the applications, then you can really enable a lot of power to people. JASON: Okay. So, then a revision, then. If
I can kind of walk this back a little bit. Is that where these generics and other advanced
TypeScript concepts really start to shine is when you are building code for writing
code. Right? Like so if you're building an application and you're gonna ship that and
that's what runs in the browser, you probably can figure out enough about how your code
works to not need any of these really advanced things.
And personally, I'm a big fan of like if you can refactor something to be just a standard
type, you probably should, right? Like don't get clever if you don't need to get clever.
But as you start writing code that's a layer of abstraction to empower other Devs to build
more things. You've found a common pattern and people are gonna wrap all their code with
this thing that adds some thing. Then suddenly you have this deeper need to like accommodate
for people are gonna do arbitrary stuff. And you want to support that without making them
do a bunch of extra lifting. MATT: Exactly. That's every Dev's code, write
code that empowers other Devs. That's why people get excited about open source work,
and they're enabling people to work faster. That's what this allows you to do. JASON: Absolutely. So, I've said the word
"Generic" a couple times, and I'm going to do a little confession here. I don't know
what that is. In the highest -- like lowest level of fidelity, what is a generic in TypeScript? MATT: Imagine you have a function. A function
takes in something and returns something. A standard type is just a static variable,
inference book, user ID and blah, blah, blah. But if you wanted to make different shapes
within them? Then in JavaScript, you would create a function, okay, I have my book, and
inside my book, I have something else. Imagine in you have like an interface for when you
return some data, for instance, from Axios or Fetch. Okay, I've got my data. But I don't
quite know what that is. So, I'm gonna make a function which takes in the type that I
want to attach to that data. And that is a generic. That's a generic type. JASON: Okay. MATT: I can't wait to explain this with code. JASON: Yeah, I'm actually wondering if we
should do that. Okay. But first, we've got one -- I don't even know if this is a spicy
question. But it's a good -- it's a good question here. So, big hairy dude, nod for the wonderful
username, asks, are generics a work around for the fact that TypeScript has locked you
into types? MATT: No, I don't think so. I think -- I think
you might mean has locked you into using types. Forced you make sure everything is correct.
I think that's my interpretation of the question. I think. JASON: Yeah. Because like my gut feel on that
is no because the work around is to just mark everything as "Any." Like if you really -- if
I have a library and I'm like, I don't want to figure out how to type this thing because
you're literally passing arbitrary code, then I would just make an argument called code,
and make it any, and you send me whatever you want. That's the work around. Generics
are almost the solution for needing that work around. MATT: Exactly. They let you hack into TypeScript
inference and let you kind of build these APIs to allow TypeScript to understand the
flow of your code better. That's what TypeScript wants to do most of all. It just wants to
understand what your JavaScript is doing. It's really good at that in a bunch of different
ways. But it has limits and these allow you to extend those limits outwards. JASON: Gotcha. I'm with you, I'm feeling it.
Okay. So, knowing that the rest of my questions are absolutely going to be easier to explain
in code, why don't we just hop into code and make that happen? So, I'm gonna take us over
to -- let me get the right things on screen here so that I'm not putting us into the infinite
loop. And then I'm gonna take us into pair programming view. And so, first, this episode,
like every episode, is being live captioned. We have Amanda here with us today from White
Coat Captioning. Taking down all of these words. Thank you very much. If you want to
watch that, that is on the home page of the site, learnwithjason.dev. And we are also
talking to Matt Pocock on Twitter @-- what did I miss? I added too many names. MATT: Probably reflected to that, anyway. JASON: I'll drop this in here. Everybody give
Matt a follow. And we're talking about TypeScript which is something you have been working on
a lot. You have this Total TypeScript site, that is absolutely gorgeous, by the way. Look
at these illustrations. Really there's some good stuff going on in here with -- MATT: I did not do this. My favorite bit is
this error message on fire. I said the illustrator, I want the error message to look like it's
coming from the depths of hell. And she did an amazing job. JASON: It's so good. It's so good. No, this
is wonderful. So, I love it. I love it. This is -- so, this is really fun. Everybody go
check this out. Make sure you get on the -- there's an email list on here, right? And that has
a bunch of free resources on it. So, make sure you get there. And with that, I have
absolutely no idea what to do next. You had me -- let's see. Let me clear this out. You
had me download this repo. MATT: Correct. JASON: And so, I've got that. And that's over
here. MATT: So, yeah. This is on -- maybe we want
to link this as well. This is the Total TypeScript, what is it? TypeScript Generics tutorial. JASON: Let me find that. It is here. Go to
GitHub.com/total-TypeScript. And then I need this name here. MATT: Yep. You got it. JASON: There it is. Okay. Let me throw this
to everybody. There you go. Got a lively discussion about generics happening. Thank you all for
getting in there. I'm looking to see if there are questions. Doesn't look like there are.
We will carry on ahead. We have this repo. This repo is part of the Total TypeScript
thing. But we are getting into generics today, which is the advanced TypeScript that I want
to learn. And I've cloned it. I have it here. So, what should I do first? Like do you want
to do a little tour of the repo? MATT: Yeah. Sounds good. The repo is simple.
If you go into the source directory, what you'll see is a bunch of different problems
and solutions. And all you've got to do to complete this repository, to complete these
exercises is go through them one-by-one, look at the problem first and then see if you can
solve the problem that's in there. Usually that problem is, there's a type error in there
or there's something wrong with it. A lot of them are not kind of explained perfectly.
Like I haven't gone through and added comments through all of these. This is still a semi-work
in progress still. But what we should see it see if you can solve the problem and understand
it. And if when you are finished, have a look at the solution or solutions if there are
multiple. The solutions are usually telling you didn't ways that you could have solved
the problem. And what you can do is when you finish this,
you can go in chat as well. We can do this together at the same time. And you can ping
me on my Discord as well. There's a bunch of people in my Discord going in this. I should
drop a link in the chat. JASON: Is that in here somewhere? MATT: It's in the linked there. It's relatively
new. I will drop a link in the chat. JASON: Okay. MATT: But that's the fundamental stuff to
do with the repo. What we should probably do is do an npm install. That's the next step. JASON: Npm install. Okay. So, there we go.
Here's the Discord. I'll go ahead and accept that invite now. I can't, because I don't
have Discord open. I'll do that later. But yeah. So, oh, that installed faster than I
expected. I thought we were gonna have to kill some time. MATT: There's not much in here. What we have
to do now is run npm exercise-01. Very nice. So, what this is gonna do, then, is it's gonna
do -- oh, we finished the exercise. That's unexpected. If you go into exercise 01, what
you'll see is that there's a challenge. And there's a comment at the top. JASON: Okay. So, we are -- all right. Let's
take a look here. How do we refactor these to reduce the duplication in the data declaration?
So, we've got user data that has data. We have post-data that has data. Comment. All
right. MATT: And then this stuff at the bottom is
basically a bunch of tests. It's testing that the types above it are what they say they
are. So, if you comment on the line 24, for instance, on that comment and just remove
that, then we're going get a type error down the bottom. JASON: Oh, and it's running as we go. Oh,
perfect. Let me pull this over here. And then I'm gonna close this up. And then -- we get
an error down here, nice. Okay. Great. MATT: So, here, our challenge is, we've got
these three objects and they all specify a data attribute. It's kind of what I was describing
before, actually. And so, there's an opportunity to dry up this code. We can take these post
data, comment data, and the other data, what is called user data, and we can -- there is
a way using TypeScript that we can dry them up. So, with your -- JASON: Okay. MATT: Extensive knowledge of Generics, Jason,
take us through how we do that. JASON: Here's my guess. If I wanted to pseudocode
this, I wanted an interface for like a data wrapper. And that will take one field of data.
And then that needs to accept options. It will take a user. It will take post. Or it
will take comment, right? And so, that would be my -- my gut. And then you're making it
sound like this should be generic. So, the data should take a generic. Okay. So, if I
do -- let me see what I can figure out. Again, I haven't actually written one of these before.
So, I'm going to say interface Data. And then generic is the T, that's right? MATT: That's correct. JASON: I put a T in. And then in here, my
new user data would be like export interface UserData extends? Implements? No, what is
it? Something. Data, right? But then it's still gonna be -- okay. I've -- I now need
to know more about how this works to start. Because if I was gonna do it this way, I'm
already -- like all I'm doing is saying, oh, this should be data. And that wouldn't work.
So, I would instead want it to be interface user, which would be these. And now I don't
know what I'm doing. MATT: This is fascinating. JASON: I need to get this into here. MATT: What I'm seeing, you're approaching
it from an object-oriented thing, it needs to extend from this. When I think you should
be thinking it in a more functional way. Why don't I drive. You are super-close. You can
see my cursor, right? JASON: Yep. MATT: We're using live share to pull me into
this as well. I'm not magically hacking into Jason's stuff. This data thing here, call
it dataWrapper. That's a perfect name. What we want to call here, we have a function,
dataWrapper. Imagine writing this in JavaScript. We have a dataWrapper, and take in the thing,
which I just call any, and we return data, which is the thing. That's the kinds of mental
model we need to have here. Which is we now need to create this on the type level. Because
this creates actual runtime objects. But we actually need to put this into the type world.
So, our dataWrapper here needs to somehow take an argument. That's the thing. And the
way that argument works is with this syntax. JASON: Ah, okay. All right. So, we've just
touched on two things that have always confused me. First, is the inclusion of these angle
brackets. And the second is the letter T. So, T, we've decided, is generic. It's a place
holder for whatever type comes in. Right? And angle brackets are the equivalent of a
function call in a type, is that correct? MATT: Exactly right. They're the equivalent. JASON: Okay. MATT: If we could write them with parentheses,
weld. We would write dataWrapper T. Like this. JASON: Okay. MATT: This is the syntax that TypeScript has
is chosen. We can call it anything. We can call it Tdata, TJason, we can call it thing.
T is just a convention. We can add as many as we want. T1, T2, T3. It's just function
arguments. JASON: Got it. Okay. Well, that's I would
say the first major lightbulb moment of the day is demystifying what the heck these are
in TypeScript. And to all of you who already know, congratulations. To everybody who just
learned along with me, you're one of today's lucky 10,000! MATT: So, here then -- JASON: And if you don't know that one -- MATT: I do not know that one. JASON: It's a comic from XKCD about learning
things. Very good. Let's -- MATT: I can't just read it live on the stream.
That's not good entertainment. JASON: No, it will be in the show notes. MATT: We've now got the function that turns
in the T data and returns a wrapper with T data here. We can delete this dataWrapper
because it's doing the same thing, but on the type world. And now we have an issue.
Which is our user -- we've got our user data down there. And we need to somehow like call
this function to return and you type. JASON: Right. MATT: So, the way that we do that is we would
say, let's say we have type, whatever. And I find myself doing this a lot in TypeScript
by the way, just creating these sort of dummy variables. And I'll show you why in a second.
We can say now dataWrapper. And we can call it with whatever actually whatever we want.
Let's just call it with a string. A string like this. And now if you, Jason, if you hover
over this whatever thing, here. You'll see, it's typed at dataWrapper string. Now, let's
actually create a variable called const whatever. And we're gonna give it a type of Whatever.
Now, inside here, if you hover over that error, what's that telling you there? JASON: Property data is missing. But required.
Okay. So, then if I come in here and I add data. And I want to do one of these. Now it's
mad at me. Because it wants a string. MATT: Exactly. JASON: Now we've got some useful stuff happening. MATT: Yep, definitely. And actually -- JASON: I think I can take over from here.
I think it just clicked for me. What I want is I want my type of user data to be a dataWrapper
of user. And then I want this here. Because now what I get is if I do a user -- user data.
And then I have my data. It autocompletes with my stuff. MATT: There you go. JASON: Wait. But it wants it to be a string,
doesn't it? There we go. Okay. So, is that how you would have done it? MATT: Well, let's check the solutions. JASON: Okay. Great. Let me comment this out.
So, it's not yelling at me. And then we'll open this up. Let's look at the solution.
Blow this out a bit. Okay. We have the dataContainer, that's what we did, user data, comment data.
Oh! Oh! I see! You abstracted it even further. MATT: I didn't -- JASON: You de-abstracted it. MATT: Yeah, I didn't declare the other interface.
I just went in and put the object primitive in there. JASON: Got it. I didn't know you could do
that. That's really handy. We have a second solution here. That an alternative approach? MATT: That's right. Alternative approach. JASON: Okay. MATT: And that one I think is declared data
contain we are a type instead of an interface. JASON: Oh, with a type instead of an interface.
And so, in this instance, does it matter? What's the difference between a type and an
interface? MATT: Types can represent anything. Whereas
interfaces can only represent objects and functions in some cases. That's the thing
I get asked most of all. Should you use a type or an interface? And the answer is, you
can use whatever you like. Basically. As long as you're consistent, it doesn't really matter. JASON: Okay. Okay. Cool. MATT: We are finished. JASON: We did it. We did a TypeScript. So,
yeah. This is -- this is really, really good. Oh, my goodness. My middle name is Danger
read my book, Untethered. I forgot that book existed. Yeah. That's a blast from the past.
I have not done that -- let's see... in like 2014 I lived out of a suitcase for a couple
years and I wrote a book about some of the stuff that I did. Like how I made that work
to get out there. That's what that book is. It's self-published on Amazon. I think a lot
of it might be outdated. and it's definitely gonna be a surprise what
this stream is about if you came looking for Untethered content. MATT: I should say my wife is writing a book
as well on -- identification. JASON: Oh, that's gonna be a lot of interest
to my partner. All right. Let's do another one. Should we
just do these in order? Or what you want to highlight. I don't think we have time for
all of them today. MATT: Number 2 is a banger. Let's do number
2. JASON: Number 2 is a bang -- all right. So,
this one doesn't have a comment on it. So, let's just look at what's going on. So, this
one -- let me do two. It's mad. It says type false does not satisfy the constraint: True.
Okay. That makes sense. And let's go and look. So, return what I pass in, unknown. It returns
it. MATT: Yeah. JASON: Why does that happen? MATT: Yeah. You think that -- because really
what we want is if you hover over, on Line 7, hover over 1 there. You would expect this
to be of type 1. That's what you've put in there, right? You've put in the literal number
1, and you would expect it to just be returned there. But actually, it's typed as unknown.
And the same is true for Matt down below. JASON: Right. Oh, and I see, down here we're
saying type of 1, 1, type of Matt, Matt. We're not getting that. That's where the false is
coming from. Okay. Now I understood how these tests work. So, I can then up here we need
to figure out how to do -- MATT: Don't look at chat. Someone's already
got it right? Chat. JASON: All right. Chat, shield my eyes. All
right. I'm gonna do an interface. And I want it to be... I mean, should it -- is it...
is it just like... wouldn't that... nope. That's not right. How do you return something
from TypeScript? Because what I'm thinking is if we did it at a function, right? It would
be you get a type. You literally just want to return type. MATT: Right. One way you can express that
is with a type itself. You can say type Fn. You go from there. I'll let you drive. JASON: Yeah. Okay. So, I do a type of Fn.
And then I need to pass it in. A T type generic. And then I need it to return. And what I don't
know is what's the -- like you can't just do one of these, right? So, what's the syntax
here? Is it -- that's not right either. MATT: I'll give you a clue. Which is you're
entirely -- well, you're on the right track conceptually, but syntactically you're in
the wrong place. JASON: Yes. Okay. So, if my type is like this,
and it needs to return something. The thing that it returns is one of these, right? But
I still don't know... wait, I know I can't just do that. MATT: I'll give you a clue, it actually needs
to be -- JASON: This is where my syntax falls apart. MATT: There's a few ways that you can express
this. But the way that chat is pointing to is -- it actually needs to be sort of inside
this function declaration here. So, it's -- we're actually going to inline the generic here.
And the way -- JASON: Okay. So, I need to do something like...
this and then this? MATT: You're like -- JASON: And like that. MATT: You're so close. I'm going to leave
this so chat tortures themselves for a second. JASON: Come on, that works. What are you doing? MATT: It's really close. Let me show you.
So, you were dead close with this, but syntactically, what we have to do is just move this to the
other side of the equals. So, this now declares it as a generic function instead of a generic
type. JASON: Got it. MATT: So, what's going on here, and actually,
you were thinking, how do I say -- how do I return this? Well, in TypeScript, you can
declare function return types using this colon here. If I say I want it to return a string,
I would do this. But actually, let's say we go to TItem here. And TItem here too. And
actually here we're going to return TItem. JASON: Right. So, that's a lot of repetition
here. Does that dry out at all? Like because I guess the thing that I notice here is that
we've got like, okay, so, we're passing an argument to our type. We're passing an argument
of the type to our function. And we're returning our function. So, there's not a way for this
to like infer where if we were to drop this entirely, it would still work? Clearly not. MATT: Yeah, if you do that, it looks for a
type in the above scope of the TItem. With the name of TItem. JASON: Okay. Gotcha. MATT: This is a type declaration. And this
is available for the rest of the thing. We have to add it here. Because we have to then
assign that to an argument. JASON: Right. MATT: So, that's the reason that's there.
We can actually drop this. Really cool thing about TypeScript is that it knows that if
you hover over this T just here, you'll see that it's actually typed as TItem. It knows
this is the thing we're inferring from. So, this is the thing that we pass in. JASON: It can do inference. But you have to
make sure that it's got the right places to work. So, this is the type declaration and
this is saying where the function uses that type declaration. But if we're just returning
it directly, then TypeScript is smart enough to know -- got it. I understand. MATT: Exactly. JASON: Okay. MATT: So, that's the type declaration. And
that is the type assignation. JASON: Now if I save this, our test should
pass. MATT: Yeah. JASON: It did. Great! And so, you've got a
couple options in here. So, this is like a hard -- MATT: Well, this option you'll notice that
there's no T happening here. This is using a technique called function overloads. And
function overloads allow you to actually make the tests pass here. Because what we're saying
is on Line 3, got return what I pass in. If we pass in Matt, return Matt. Up here, if
we're saying if we pass in 1, return 1. And then down here, we say, we actually implement
the function and we say, return what I pass in, which is unknown. And we return the T.
And so, this does make our test pass. JASON: So, this one feels a little bit like
when you write a function like is odd. And then you start doing like if... MATT: Yes. JASON: Yeah, like one of these. MATT: That's such a great way of putting it.
Yeah. That meme from whomever posted it. Right. JASON: Right. And you find yourself writing
all of these like. Ope. Better do another one. Ope, better do another one. MATT: The thing I remember, it was such bait
for the mansplainers. Did you use the module? So good. JASON: What was that? Cat Maddox? Such a good
post. I'm not going to be able to find that actual post. But I am gonna kind her Twitter
because it's amazing. It is full of -- full of good information. Cain. Oh, Cain now. Okay.
Yeah, Cain is freaking hilarious. So, all right. So, and then what we've got
here... is the generic. So, I'm gonna undo my work in here so that we don't get... all
right. Oh, here's our -- here's our generic. Great. And that's what we ended up doing.
So -- MATT: Yeah. There's one thing to note in here
before we continue too. Which is that TypeScript give use like really good -- oh, trash is
raiding. What's up, trash? JASON: What's up, trash. Thank you for the
raid. Welcome, everyone. MATT: He considered himself an acolyte wizard,
going through the challenges. Like a Hogwarts first year. JASON: Gotcha, gotcha.- MATT: He's passed his AWLs. He's smashing
it. Roll through this stuff. JASON: Great, great, great. MATT: So, the thing to look at here is that
if you hover over -- sorry, let me get back to the file of mine. Oh, we're in the solution
2, aren't we? JASON: Do you want me to move back? MATT: No, if you hover here. Return what I
pass in. You'll see that you get the generic actually get locked in there. Which is super-duper
useful. JASON: Yes. MATT: And create another one of this, const
obj, returnWhatIPassedIn. And 32 -- I don't know why GitHub Copilot thought was 32. And
then you pass in, so, TypeScript is really good at giving annotations to tell you what
generic has been locked in at what time. JASON: Sorry, the chat is just giving each
other crap and it's making me laugh. And this is the stuff that I think is really useful
here. Because this is the piece that's magical, right? Because later, when we go to use this,
it autocompletes for me based on what we're using, right? And so, that -- that's the part
where... like early on when people were telling me about why I should care about TypeScript
and they would talk about, oh, you want like compile errors early and, you know, you would
want to have that type safety. I don't care. I don't care. I don't care. I'll fix that
error. When the bug happens, I'll fix it. I understand academically, eat your vegetables,
write your tests, declare your types, all those things. But also, I'm under deadlines
and I don't want to do it. Then someone showed me autocomplete. And now I care about TypeScript.
The idea that if I write these types, I don't have to go look at the code declaration to
use it. Absolute game changer. That's the part where I started including first the JSDocs
syntax, defining my params to get type completion. And then I saw, that's not going to bed a
flexible as you need, I took it one step forward. I started writing actual types. If I'm writing
something I'm gonna share with somebody else. I do it in TypeScript. Not because I care
about type safety, but because I care about this autocomplete. I want people to use it
without having to think about it. That to me was the real killer feature. MATT: It's a way of passing on the DX bat
to your friends. You get to say, hey, I built this cool thing. You don't really need to
looking at the docs to know how to use it. You can just figure it out. Anything you get
from it is going to make the rest of your codebase better, easier to use, safer too. JASON: Yeah. And that -- and there are like,
I felt all the benefits of like you write your types and then you change something and
then get a type error, oh, I forgot to update that piece of code. I'm not saying you should
not care about that stuff. You should. But where it emotionally struck home for me was
autocompletion. This is why people care. This is the magic. MATT: It's the feeling of, oh, my teacher
wants me to do well. Sure, they were being hard on me amount the start, but actually
they do really know what they're doing. And thank you. JASON: Yes. Absolutely. Okay. All right. So,
what should we do next? Which one should we tackle next? MATT: Let's have a look. Yes, let's go on
to number 3. Number 3 is a good one to introduce some nor generics stuff. Let's expands on
what we've got so far. JASON: This one is absolutely riddled with
errors. So, let's take a look. It's expecting that our string set will be a set of strings,
set of numbers, unknown set of unknowns and we are seeing -- let's see. We get... we create
a set and that's supposed to be a set of numbers. This says we want to pass in a number. So,
here. I want this to take a set of numbers so it should return -- okay. I think I have
an idea. So, we -- we are passing in our generic here. So, we'll say type. Right? And then
what we want is to return. And I'm making a guess... did I do it on the first try? MATT: Whoa! JASON: Did I just learn TypeScript? What up,
chat? MATT: Trash, this is how you do it. Trash,
by the way, this is how you do it. JASON: Okay. That's -- okay. All right. I'm
just gonna take this little clip, I'm gonna put it up on my wall. MATT: Can I say you not only -- you not only
do it first try, but you actually found a solution that I haven't included.
so, there we go. JASON: Okay. So, here was the guess that I
had to take. If I tried to use set, it would have a built-in type. That was -- all right.
So, maybe let's walk through a couple other ways that we could do this. Because I apparently
found something that you didn't do. So, should we look at the solution or do you want to
talk through another one? MATT: Let's look at the solution. JASON: All right. Here we go. MATT: The solution is too. What's didn't between
this and your solution? JASON: Ah, so you dropped it in here. Here
we're telling the set itself to accept the generic. MATT: Yes. JASON: And we're saying what's returned will
return the generic? MATT: TypeScript can do it either way. Refer
the return type, or refer it from the return type from what your code does. JASON: Both of these look pretty clean to
me. I think this -- yeah. I... I don't -- I feel like I would have gotten here eventually.
But here's the other thing. If this wasn't a built-in type, if VSCode didn't just know
what a set was, this would be way easier than trying to figure out how to get a type from
the set. MATT: Yes. There's a couple of wrinkles with
this whole setup -- setup. Which is that if you notice that T type here inside -- yeah.
Where are we? Yeah. Here we're not actually using it in any of the arguments of the function.
So, it's not actually being inferred anywhere. If we were to pass like ATType here. We could
actually remove and say a, as string. Hello. And now it would infer string from the argument
that's being passed in. So, because we're not passing an argument, we're actually using
this generic slot as a kind of -- a slot where you can pass in a type yourself. JASON: Right and so, what we're saying is
you have to decide here. MATT: Yes. That's it. JASON: If you don't use it, it will be unknown. MATT: This is similar to if you have used
React. It is similar, you have a use state here, ID, set ID. And you would have to manually
pass in here that it's a string. So, generics are interesting. They can be inferred from
TypeScript itself. And from just like the call. But you can also override them at the
call site and manually pass in your own stuff. JASON: Gotcha. MATT: And Jason, dunk on Trash. Yes. JASON: I was dunking on the entire chat, Trash,
not you specifically. But I mean, I'll -- yeah. MATT: I got you, man. You're gonna raid us,
you got to be prepared for this stuff, you know? JASON: If you hover over set in the code,
it will show you the generics available? MATT: Yep. That's true. If you -- yeah, command
-- and here's something crazy you can do. Try command clicking on set. JASON: Command clicking onSet. Here we go,
here we go. MATT: Double click inside there to open it
up. Yeah, perfect. We are now in lib.es2015.collection.d.ts. They cannot follow you, you are in the deepest,
darkest dungeon of TypeScript. These are the type definitions for the JavaScript language
itself. We're down with the Balrogs and down with the scary beasties. And we can see here
all of the methods that are implemented on set. We have set add, set clear, set forEach.
And here's what's crazy. We can comment these out. Comment out lines 59 to 64 or whatever.
Come on, let's break it. Let's break JavaScript. And now try and go back to the problem.ts
file, the one just in the tap over there. And now try to do anything on these string
sets or number sets you'll see stringset.has, foo. And set doesn't exist anymore. JASON: I hate it. Okay. Then it comes back.
So, if you ever want to, you know, if you do end up being one of those folks who works
in an office. I guess the 70% of who are not -- well, not me. I don't work in an office.
If you are back in an office and you want to ruin your coworkers day, Matt Pocock has
provided the solution. When they retaliate by trying to ship their enemy's glitter, give
them Matt's address. MATT: Come on. I hate getting glitter in the
mail. Really rough. Sequins in the post. JASON: I don't know if there was a service
-- there was a service for a brief moment called Ship your Enemies Glitter. They would
send an envelope completely full of glitter. I think it was something where when you opened
it, it would make the glitter come out of the package. Yeah. It's a full rough. MATT: But yeah. Well done on Number 3. You
smashed it. JASON: Smashed it! All right. Now -- should
we just keep going on 4, 5? Where do you want to go? We've got -- time checking, we've got
about 35 minutes left, so, if there's anything you really want to show, let's get there. MATT: This is lame, but number 4 is good to
go to next. JASON: Okay. Fetchers is an object that you
can optionally set keys for the route names. This is the route names, how do but prevent
the user from passing fetches that are don't exist in the routes array? Oooo... okay. I've
seen a thing in TypeScript that I only lightly understand where people do things like pass
in key of or value of. Is that the direction you're trying to go right now? MATT: I'll give you a clue here. The thing
that the question is asking you for is impossible. JASON: Oh, interesting. Okay. MATT: What's it's asking you to do is try
to find a -- an API that would handle this. Because if we think of a generic, a generic
has to exist in the context of a function, right? It has to be something that either
you pass in or is inferred and then returns something else. And there's like -- I'm expecting
you to the to be able to solve this one, by the way. So, we can like shift over to the
solution. This is like an extremely tricky one. This is interesting. JASON: Wait, wait. No, we can do it. We can
do it. If we pass in -- can have MATT: I'm just gonna shake the window. JASON: Literally leaving me to my own devices.
We're about to learn TypeScript with Jason. I'm gonna make it up on the spot. It's gonna
go great. What I want to do is if I want to pass in -- if I want to pass in the routes.
And the routes are gonna be one of these... pronounced "Trout." But then I would pass
in one of these. No. One of these. No. I'm already lost. Okay. So, let me pseudocode
what I'm thinking, because that will be easier for me than trying to write TypeScript since
I don't know a lot of the syntax. So, what I'm picturing here is that we're gonna return
these objects. And so, like each of these would be -- would be like... routes.map then
you get your route. And then that's gonna return back... the... return. Right? So, we
get our actual route -- or no. It would need to be like an object. Which would be route.
And that passes in this function. I did something terrible here. Oh, I've double-wrapped it.
Okay. So, this is my -- this would sort of give
us the ability to do this and then if I knew that this was gonna a route... and then each
of these... needed to be -- oh, oh, oh. Wait. Ah. Ah? No. Not quite. Not quite. MATT: What you're building here is really
cool because it shows the constraints of the problem. If you think about TRoute here...
this declaration, this generic, is only valid for this scope. It's gonna be out of scope. JASON: Oh, it won't come down. MATT: Exactly. It won't -- like you can't
pull it in from outside the function. So, we somehow need like TRoute to be up here
in the top level. JASON: Gotcha. Okay. Okay. MATT: This isn't valid syntax, right? So,
how are we gonna get around that? JASON: So, now we need to start identifying
a type up here. Or I guess -- whatever -- interface. And then... this would be config. Not would
take a TRoute? MATT: Yeah, definitely something there. You're
on the right track. Getting warmer. JASON: Okay. And then I get my routes... which
is gonna be... the array of routes. If I'm right. MATT: Correct. JASON: Accurate syntax. And then it would
be like fetchers. Would need to be one of these. MATT: You don't need to re-declare the TRoute
again because it's already in-scope. JASON: All right. So, I'm gonna get my routes.
Which would be TRoute, like this. And then that gonna return... wait. Each of these would
return back... the... oshsh. My syntax is failing me. How would I declare this that
is? MATT: Yeah, this is tricky. What we want to
express -- JASON: But that's still wrong. That would
let anything come in. Is there a way to get like what was sent here to pass in here? MATT: Yeah, like -- JASON: Like a value of or -- because what
I would do if I was actually trying to check is like, if, route... or it would be... like
route -- would have to rewrite some code here. But... you would do something like that to
say like if what you're being passed is included in this route up here, then -- then you can
go. Otherwise you would need to throw an error. So, I want to do the same thing by getting
whatever the value of routes is and then passing that in here. Which is exactly what you've
asked me to do and I'm now unable to do it. MATT: But like, this is hard. Because what
we're doing here is we're saying, this slightly changes the API down here. Right? Because
-- JASON: Right. MATT: Because down below, sorry, in fetches.
No longer saying this is an object, it's a function. JASON: But I'm not allowed to refactor the
code. MATT: You are a bit. But the function is in
the wrong place. GetFetchers is no longer needed, basically -- let's, I think. I think
if we go to the solution, it's going to elucidate what I'm saying better than I can say it.
Because you are super-close. JASON: K in TRoute! I knew it was possible!
Dang it. So, yes. All right. So, this is the -- the value? Is that right? MATT: This is kind of like -- JASON: X and Y is the -- is a -- is -- so,
when you do like an X and Y... right? It would be like that would do the 1, 2, 3, this is
the X and this is the Y. MATT: Yeah, exactly. Sorry if you can hear
a storm blowing outside. It's absolutely crazy here. So, yeah. What this is doing is, it's
basically creating an object out of the routes. So, if I were to create a type of route, for
instance, it's going to be fruits or, sorry, yeah. K in apple or banana and we create a
string here. So, we say const Fruit is fruit. And inside here, it's gonna ask me for apple
and then banana. And I can add things to this. I can add things to this union. I can say,
cheese I know isn't a fruit. But that's the only thing I could think of. And this is basically
creating a object from these types here. And TRoute is gonna be a union of the things that
we pass in. JASON: Right, right, right. Okay. So, more
practically, what that means is, when we pass this in, right? We get these values. So, what
this is becoming... is... this. MATT: Exactly. JASON: That also works because of the way
that we've defined this code. But it's not flexible because, again, you've hard-coded
everything. MATT: Exactly. So, that, then, if we revert
that, that TRoute represents the union of those three thing there is. And we can see,
though, that it's not quite perfect. Because if you look down at config objects here. And
actually, if I add in a -- in fact, this is gonna blow your mind, first of all. If you
go into fetchers and try adding a new one to that object. First of all, you can see
all the autocomplete in there. Try adding a new one. JASON: The autocomplete is already amazing. MATT: And you see all the stuff we've added
in there, it's all in there. JASON: Beautiful. Absolutely beautiful. MATT: But it's actually not working as well
as we think it is. Because if I add something else to this -- if I go like, not a route.
Then it's actually going to yell at me. Because it's saying if you hover over that error,
not a route is not assignable to thingy, thingy, thingy. Because we're actually passing in
the generics up here. JASON: Oh. Wait. And we can't just leave that
out? MATT: No, if we leave that out, everything
falls apart. JASON: But we -- what if we do... that? MATT: Then we don't get autocomplete down
here? JASON: Boo! MATT: Boo! Yeah, quite. The lesson here is
generics cannot exist at the scope of objects. They have to exist within a function. So,
let's have a look at solution two because I think you're gonna like that one a bit better. JASON: Okay. So, we've got fig object, TRoute.
Extend string. Okay. And now this -- by extending string, we're saying the route has to be a
string. That's what we mean when we say "Extends." MATT: We're constraining it to be a string. JASON: Got it. Sets up a route here, and route,
and allowing that back, that's fine. That's not really what we're worried about here.
Make config object. So, we're doing -- we're passing in the TRoute, extended string. And
then we need to get back a config object that gets passed a TRoute. Right? MATT: We're not getting that back, we're actually
passing that in there. JASON: Ah, right, right. So, I see. We're
like doubling up here. We've got -- we've got a generic here. But we have to get that
generic into here so we're -- we're kind of doing double duty. Do we need a double...
the double extends? Or would this one kind of pass through? MATT: Try deleting -- let's try deleting this
one and see what happens. JASON: Okay. Now it's unhappy. MATT: And it's also unhappy on Line 13, which
is the more crucial error. JASON: TRoute does not satisfy the constraint.
Okay. So, undo that. Now, if I do this... unhappy. Not assignable to type string or
number or symbol. MATT: Only strings or numbers or symbols can
be object keys. And we're creating an object out of TRoute there. JASON: Okay. So, it won't pick up this inference
if you pass through. MATT: Correct. Yep. JASON: Okay. So, we've got that there. And
then, we have this function to make a config object which is more of what we wanted. Which
is then picking up all of these. And if I delete this... now it says: Is not assignable
to type -- a little messy. But... it does now give us our autocomplete and we didn't
have to hard code those anywhere. MATT: exactly. And if you add a new route,
it is gonna give you more things to autocomplete. JASON: We can get rid of the error... it's
cool. And in here... do one of these and there it is. Okay. So, this feels extensible in
a way that doesn't -- the thing that always makes me nervous about TypeScript is I don't
know how to do this, right? So, the way that I would have solved this problem is by literally
hard-coding all of those routes in. And then you have that problem where you've got two
versions of your hard-coded routes. It just sucks, right? My way of solving is would be
just write the code to do that check in here. Make that a function. Like what I started
doing originally. This -- this feels nice. Because it does what we expect. MATT: Exactly. And it actually lets you pull
more logic out of your runtime code into your typing code. We're shipping less logic to
the browser or to the whatever because it's more constrained within our type code. And
now that make config object, that could live in a library. That could be something you
distribute across your organization. And suddenly you have this type safe router fetcher thing
that's available to anyone who wants to use it. You notice when we use it in Line 16 onwards,
there's no type definitions here. Nothing you need to add to it to make it do its thing.
It's doing the inference for free. JASON: I saw a question from the first time
chatter, Charles, the way we're typing on the screen, we're using VSCode Live Share.
That's a plugin that you can get... here. Where is the actual -- here. So, this is a
pretty cool service. If you've never used it before, definitely give it a shot. Tons
and tons of first-time chatters. Welcome, everybody, if this is your first time seeing
the show. I'm excited to see you. Hopefully you want to stick around. Remember, you can
always, you know, subscribe to the channel, follow, all those sorts of things so you know
when we're going live. We have two of these a week. It's a lot of fun.
Okay. What should we do next? We have about 20 minutes. MATT: Let's do one that... um... okay. Let's
go to number 8. This is a really famous error. Because it gives you a really hard to understand
error message. And understanding why it gives you the error message is really crucial to
understanding generics itself. JASON: That was which number? Sorry? MATT: Number 8. JASON: Number 8. Sorry. Let's take a look
here. We've got object as const. Interesting. Object key of, type of -- a lot going on in
there. So, wait, keyof, typeof object. So, this is the object. keyof, typeof would give
us the union of ABC. MATT: Exactly, hover over obj key. JASON: That's exactly what we did, great,
great, great. And we want to get the object key, TKey extends object key. And then A -- A
is not assignable to type T key. A is constrainable to the constraint of type TKey, but TKey could
not be initiated with a didn't subtype of constraint... MATT: Yep. JASON: So, that means this -- this matches
technically. But if somebody put in a B, it would break. Because it would match this but
this one is being stripped down to just A, is that correct? MATT: Sort of. JASON: No? MATT: I'm gonna let you flail a bit longer. JASON: Okay. So, let's think a little bit
more here... type of 1 by default should be here. But it's not doing it. Okay. So, it's
not picking up our default value. And it's not picking up our default value for reasons
I don't understand. Because... I actually have no idea. Why doesn't it pick this up? MATT: This is really brutal. This is like
a really hard one. If you hover over one by default there. This doesn't really give you
a clue. But it tells you that if we don't pass in our get object key there, it's giving
us either one or two or three. Because that's what happening -- JASON: Right. MATT: If you hover over this part of it, it
will show you that when we pass in nothing to this function, the generic gets locked
in as the widest it can possibly be. JASON: Right. MATT: Because you see here, the generic is
extends object key, we're making it get object key. It's assignable to, or you're assigning
to, then it defaults to the widest it can possibly be. JASON: Right. Because we don't actually pass
this argument, this assignment doesn't happen. MATT: Yeah. JASON: Is that right? MATT: It doesn't happen -- and it happens
at the runtime level, but it doesn't happen at the type level. JASON: Right. MATT: And so, hover, Tkey, could be instantiated
with either A or B or C. And the different subtype of constraint is basically saying,
it could be either B or C, and this default doesn't account for that.
So, that's really hard to deal with. And there is a trick to do it, basically. And it will
get you out of a lot of tight spots. And this always happens whenever you try to assign
a default parameter that is more narrow than the generic slot that it's supposed to inhabit.
And I can tell by the tone of your voice that you want me to show you the solution instead
of like -- JASON: I'm gonna be honest. I don't even know
where to start. Because like what -- because like I know what you're saying. We need the
generic here to somehow be more narrow. But I don't -- I don't even know where I would
begin. MATT: Absolutely. So, the way to think about
this best is if you actually go to -- I think number 2? And then solution 1. You remember
we returned to this idea of function overloads. Which is if you call a function in one way,
it's gonna return one thing. If you call a function in another way, it's gonna return
another thing. Well, one amazing thing about function overloads is you can actually provide
different generic parameters based on each overload. So, you can say, I just sort of
scaffold some syntax here. We can say, let's imagine that A, a function gets something.
We can say, if you pass in, a is gonna be 1, then you return 1. And I have the implementation
down here, which is any. And then return as any just to shut up TypeScript. JASON: Okay. MATT: So, if we pull at something. Say const
result = getSomething. And then if we say -- if we pass in our 1, if we hover over the
result here, that's gonna be typed in as 1. JASON: Okay. MATT: Sorry, move out of the way. If we add
in another, let's say you pass in, I don't know. A, which is gonna be Matt. B, which
is gonna be Pocock. And we're actually gonna return here matt pocock. JASON: Okay. MATT: Matt Pocock. And then if we wanted one
of these signatures to be generic. Let's say I wanted to add someone new to my family.
And I say TName. And put in TName here. And TypeScript lets you do interpolation here.
Tname Pocock. And I need to extend string. JASON: Okay. So, we throw in another name.
Like... MATT: Exactly. And we could end up with Robert
Pocock. Where if we just add 1 back in here and hover over it, then it actually doesn't
even try to fill the generic. JASON: And if we throw in just the 1, naw,
get out of here. MATT: It's going to try to make it fit. But
it's going to error it here, if we try to say, Nancy Smith or something, then that's
gonna error too. It should. Not erroring -- JASON: Or just overloads is... MATT: That's really weird that it's -- I think
we found a bug in TypeScript, actually. That's no good. Well, okay. Let's go with that quickly.
But the basic idea here is then we need an overload that -- because there's two different
cases here. There's the case -- JASON: Right. MATT: There's the case where we actually pass
something, and the case where we don't pass something and on the type level, those are
two very, very different things. So, I'll do some refactoring too. JASON: Right. So, in this case... we need
key A. And that's gonna return A. And then the other one is the generic. But it doesn't
like that. MATT: It doesn't like that. We've kept the
default parameter, then. JASON: Oh, so we can just drop the default
parameter. MATT: Exactly. JASON: Wait, wait, wait. But don't we need
that default parameter? MATT: We don't. There's actually a slight
mistake here. Which is we're trying to look at this one here. And actually, in this case,
when we don't pass anything, we don't pass any arguments. We shouldn't put an argument
here. JASON: Oh, you're right. You're right. We
should -- oops. We're gonna go here. Okay. So, that's happy. That's A. But now everything
else is mad because it wants these. So, I have screwed up our overloading. MATT: We still need to get rid of the default
parameter too. We don't need that anymore. JASON: And now this is unhappy because, not
compatible with its implementation. And that's because? MATT: That's because the way that function
overloads work is each one, if you imagine them stacked up, they have to be compatible
with their eventual implementation. And the one at the bottom is where you actually implement
the function. The other ones are kind of reflections above it that say, you can do this or this
or this. JASON: Okay. MATT: But the way that we would make these
compatible is we would say, you don't have to pass TKey here. You can actually do this. JASON: And make it optional. MATT: Make it optional. You know what? I think
we need to brute force this a bit. By the way, this return type here, we're not actually
returning A. Because we're returning -- we're using the A key to access the 1 here. JASON: You're right. So, this would instead
be a 1. MATT: Exactly. JASON: Right? MATT: And what I'm gonna do instead, I'm gonna
just move this one up to here and say this is going to be -- just get this crud out of
the way. And the key is TKey. TKey. Then we're going to say, typeof obj. We need to kind
of like declare the return type here. I'm gonna call it typeof obj TKey. That syntax
there, how scary does that look? JASON: Looks pretty terrifying. MATT: What we're doing there, is if we say...
let's say we go type whatever equals typeof obj. And pass in key here. What type is that
going to be? JASON: It's going to be a number, right? MATT: A number or -- JASON: A is gonna be there did -- oh, the
literal 2. MATT: Exactly. JASON: Got it. Nice. MATT: How about if we pass in B or C here? JASON: Then it's a union. MATT: Correct. JASON: Okay. MATT: In this case, we know that TKey extends
the object, because it's there. And that means that what does this then mean JASON: This means that basically whatever
is passed here, we expect it to be the number literal that's inside. Okay. MATT: Exactly. Yep. JASON: Now, it's still unhappy because...
implicitly has an any. Can't be used to read-only. Okay. So, the piece that I'm missing here
is that I don't know how -- like because we need this code, right? MATT: Yeah. We didn't -- yeah. We do need
this code. What we... what we now need to do is... let's see. Why is this erroring here?
String... ah! The reason here is the key here is typed as if you hover over that word there.
Is it's typed -- JASON: Why are you a string? MATT: Why is it a string? How can we make
it more narrow than a string? JASON: One of these, right? MATT: Yeah, except we don't -- inside the
function signature itself, we don't need to re-declare -- the way that function overloads
work, you don't need to -- they get sort of wider as they go down, basically. This TKey
here, this one is only relevant for this declaration there. JASON: Okay. MATT: And so, actually what we want to do
is instead of TKey, we just want to put it to the constraint. Which is ObjKey. JASON: Okay. MATT: Which is up here. JASON: And now it's happy. MATT: Now it's happy. We have manually gone
through and specified all the different cases we have there. Before what we had, on the
type level, okay, I want you to think about A or B or C, but by default, only do A. And
trying to do that in a tiny syntactical space. And TypeScript didn't understand what we were
doing. JASON: Yeah. This is... oof. Yeah, it goes
fast. But yeah. There's like a whole... whole chat going on the side here. I'm just checking
to see if there are questions. I don't see questions. And chat, if you are able to -- if
you have questions, definitely let us know. Are you able to comment out Line 14 and have
it work? No. Because we have to have this base case of no argument. Because this one
expects that there's an argument. So, we are -- to just explain this back and make sure
I understand it, our function here needs to accept a value. A default value is A. And
we need to make sure that that is within the constraints of the object key, which is our
union of our available keys. And if we add another one down here, this will expand to
include D, right? And then so, when we go with zero, zero arguments.
We know that we're gonna default to A. So, we need to return that. Because that's what
our function does. Right? So, our type becomes 1. So, in this default case here, we know
that we're getting 1. And then for all these other ones, our key, what we're sending, needs
to be one of these values. And then we are returning whatever the object contains for
that key. And that allows us to then set our deal. MATT: Exactly. JASON: Okay. My brain hurts. MATT: Let me show you just like -- yeah. This
is tasty stuff. Like this is -- there's -- this is like solving one of the biggest pain points
in TypeScript Generics, right? It's that instantiated subtype thing. When I Tweet out, what's your
least-favorite error message to see? This is the one that people reply with. Trying
to work around a horrible error message in TypeScript. And we have a repeatable way to
do it now. Which is great. But yeah, I think this is a good place to end on. Because this
is the really hard core stuff. JASON: One more question here, why couldn't
we just delete the base case and make the key optional? That's a good question. If we
take this, and see if we can do this in 1 minute because we're out of time. So, get
object key. And that would be TKey extends object key. And that's gonna take the key
which is optional now, and that takes a TKey. And it's gonna return type of object, TKey.
And we want the default to be A. Okay. The syntax. MATT: Briefly... JASON: Okay so, now it's sort of -- sort of
happy but it gives us the exact same error. MATT: This is where we started, basically. JASON: So, it looks like we have to do this
overloading because this inference too hard. Like there's probably a way to make this smart,
but like not -- basically, this overload is what makes it all work, right? MATT: Yep. To answer American250's question
too. Another thing you could try and I have tried -- you can actually give -- whoa! No! JASON: Oh, sorry, sorry, sorry. MATT: Come on! Is you can actually give this
like -- you can give the Generic here -- can you tell I was a voice coach, by the way?
Drama. You can give this a default case, right? You can give Generics a default case. Which
is really interesting. This feels like it should work. But it doesn't. Because it's
still the same problem. You're still coming up against this could be A or B or D or C
or D. It's kind of like you're trying to override this with this in TypeScript. JASON: Hm. Yeah. Okay. So, this feels like
one of those cases where like my instinct would be to refactor this instead of trying
to -- instead of trying to type it. I would be like, okay. Let's just write this function
to be simpler so that we don't have to do this. MATT: Exactly. JASON: And, you know, it sounds like we're
out of time. I'm going to start signing us off. This is the sort of stuff -- are you
covering the like -- sure, you can, but maybe you shouldn't part of TypeScript? MATT: Yes, absolutely. TypeScript imposes
API constraints on you. And mostly those are good constraints because they force you to
do more functionally. They force you to do more in terms of designing your API so that
inference flows through your app. But sometimes they're bad constraints. And so, there are
exercises in my course where we look at something that is impossible, you know? And ways to
work around it. But also, you should maybe be thinking about designing your API in a
slightly different way to take advantage of TypeScript instead of fighting it. JASON: Right. Right, right. Okay. Perfect.
So, with that being said, I just dropped links to Matt's Twitter, to Total TypeScript which
is if you want to be further than this, go sign up here. And to the repo that's full
of the -- the challenges and solutions. So, go and run through those. This show, like
every show, has been live captioned. We've had Amanda from White Coat Captioning, thank
you very much. That is made possible through the support of our sponsors, Netlify, Nx,
and New Relic, making this show more accessible to more people, which I appreciate very much.
While checking out the site, make sure to go and check out the schedule. We are going
to reschedule with Will, but I will be on Thursday. We will do something. I have a couple
weeks off, a company All Hands and then vacation. And then back strong with a bunch of new stuff.
Not all on here, add on Google Calendar, follow on YouTube, all those things so you get notified
of new episodes when they're scheduled. And whenever they have gone live wherever you
want to consume your media. Any further words before we're done? MATT: Thank you. It's been a wonderful chat
and wonderful audience. Good view numbers. Everybody getting excited about TypeScript.
Love it. JASON: Thank you for hanging out. We are going
to find somebody to raid, chat. Stay tuned -- Ben is live right now. We're gonna go raid
Ben D. Myers. Thanks all for hanging out. We'll see you next time!