The Standup with ThePrimeagen - WTF Typescript?
Episode Date: July 10, 2025Trash presents his Typescript presentation from Render ATL (Sorry if you are audio only listener) 🔗 Sponsored by Sentry https://sentry.io | Code breaks, fix it faster 📌 Chapters: 00:00:00 - I...ntro 00:00:42 - Who’s actually a “web developer”? 00:02:31 - Trash’s live intro & TypeScript presentation setup 00:03:59 - Sentry #ad 00:04:38 - Excess property checks in TS 00:09:42 - WTF: Object spreads silence type errors 00:14:30 - React prop spreading madness 00:18:22 - Structural vs duck typing debate 00:24:00 - WTF: Enums and why everyone hates them 00:27:45 - Enum strings vs numbers behavior 00:33:10 - Why enums in Node are cursed 00:36:55 - Safer enum alternatives using const objects 00:43:10 - TS quirks: Boolean filter keeps undefined in types 00:46:40 - TSReset: The “CSS reset” for TypeScript 00:49:01 - WTF: Empty object {} lets everything through 00:52:32 - Doom rendered in TypeScript type system 00:54:00 - Type-level math in TS (it’s real) 00:56:33 - Scaling hell: TS performance bottlenecks 01:00:01 - Flamegraphs and fighting TS perf without docs 01:04:55 - TS inference vs explicit typing in large codebases 01:09:30 - Prime’s favorite TS bug strikes again 01:14:01 - Casey breaks down the compiler logic 01:17:50 - Is this a TypeScript bug or a feature? 01:22:44 - Final thoughts and community love --- **Topics Covered:** • TypeScript excess property checks • Enums vs object literals • Structural vs duck typing • TS compiler edge cases • WTFs that break your mental model • Type-level math • Performance debugging hell • Massive monorepo pain points • The one TypeScript bug Prime can’t let go
Transcript
Discussion (0)
This is the episode, though, where Casey's going to find out that, like, he's been calling all three of us web developers, but Trash is the only one who actually knows how to develop for the web?
I mean, you can use TypeScript on the server.
Okay, so Trash is a web developer, and you guys are web developers, is what you're saying.
Yes, thank you.
I'm just Enormi, but everyone's so bad at their craft that I just rise to the top.
Which is really funny.
like normal and that's like really good.
Nice. Then they give you talks at Render ATL.
Exactly. They're like, this guy's a genius. I'm like, look, I just know the basic.
Oh, hey, Prime. Oh, howdy-duty?
Hello? Hello?
Are you ready for some TypeScript Prime? I'm so ready for TypeScript.
Yeah, yeah, yeah, yeah, yeah, yeah. Uh, anyway, sorry. All right, hey, I'm going to do the introduction
right now, okay? Okay.
Okay, you're freaking out.
Okay, okay.
Hey, you got this.
Trash, you're the man.
I'm so ready for this.
Tresch, you are awesome.
I'm freestall on this.
Okay, this isn't going to be.
Trash?
All right.
Just go, just go.
Here we go.
Today's, let's see, today's stand-up is a special one.
We have with us a legendary engineer.
Chris Batista, you probably know better as Trash Deb.
Going to give us an amazing presentation about TypeScript,
build system, speed, performance, usability, all the good stuff.
Trash, unironically, drinks from,
Mason jar just like me. So good job, Trash. Really appreciate that. Hey, cheers, buddy.
But second off, he's actually a very talented engineer. And if I were to have
typescript problems, there's the first person I would call would be Trash. He's actually quite
excellent. So I'm very excited for this talk. And I'm even more excited about Casey being
confused by the fact that there is JavaScript that's not JavaScript that can't run. That takes a
long time to make into JavaScript. And this is just, I'm very excited about this whole thing. So this is
going to be excellent. So trash, could you please take it away for us? So this is how I'm going
to approach this because I want to get through like the less interesting parts I feel like in my
opinion and get to get to the stuff that I think case will get interested in. So I'm going to
pick pieces of my presentation to show you just stuff like weird quirks that happen in TypeScript
and then we'll kind of like go into the more in-depth stuff and kind of just like we'll probably
just end up freestalling at that point. And this is also weird for me because I only have
one monitor. So I have to like, you're basically going to see me like finangling all this stuff
left. So Josh or whoever's going to edit this can, can edit it. So, so first things first is like
TypeScript is an interesting language where in my opinion, there's like a spectrum of like
skill level. I don't want to say skill level, but just like familiarity with the with the language.
And I feel like most people don't take the time to actually jump over that hurdle and get to the more
advanced stuff, not because it's their fault, but more of like the work that they're doing
doesn't really actually call for it. So like the talk I'm doing, it's very high level
because I think most people fall into that kind of bucket. But then there's pieces of my talk
that I kind of skim over because I don't want your eyes to glaze over during a presentation,
right? And I was also at the end of the conference. Trash, can I interrupt one second?
Yeah, yeah. What is the title of your talk? It is called WTF TypeScript. Literally.
Dude, I had some bomb-ass memes during this presentation that I was so,
terrified that weren't going to land. But thankfully, everyone started laughing and I just like,
then all my like nervousness went away. I like called it out in the beginning. I was like,
guys, I'm like freaking out right now. And I was doing a sound check. The MC was introducing me.
And I had a like a remote mic on my shirt. And when I was sound checking, my mic wasn't that loud.
So while the MC was introducing me, I went skirt into the mic and it just like filled up the
stadium, not the stadium, but filled up the room and everyone just like looked at me and I just like
Frozen Terror. It was like the worst thing over. But anyways.
Did you find the errors? I don't even know what they look. What do they even look like?
They're in the phone. In the phone? Yeah, they're definitely in there. I just don't know how we
labeled them. I got it. Don't worry. You gotta figure it out. We're running out of time prime.
You gotta find them and meet me at the stand-up. Roger.
All the context you need to debug your problem, because code breaks. So fix it faster with Century.
I want to show Casey excess property checks.
Oh, no, on this screen, it kind of like makes things crap, but it's fine.
So excess property checks.
So check this out.
So this is a type.
So you never seen TypeScript before.
I've never.
Okay.
So the function looks like JavaScript to you.
The only difference between, like, JavaScript and TypeScript, in my opinion, you see these little colones here, and that's your type afterwards.
And then the type definition is at the top.
Hopefully that's pretty obvious, right?
So you have type person.
It's an object like structure and has two properties.
First name of type string.
age of type number, and then this function, obviously,
will take it some parameter and it's a type person.
So the interesting part, what TypeScript is, okay,
if we call it like this, this looks normal, right?
This doesn't air out.
Business as usual, if you actually work in the TypeScript ecosystem,
if you pass in an extra property,
it's going to actually break with extra prop.
So hopefully this all makes sense.
Like this is actually like aligning with most web
developers mental model.
But what's interesting here is,
you extract this object, the type error goes away, right? Which isn't the biggest deal. So the
difference here is like typescript is structural. So as long as it satisfies the contract of first name
and age, type script's like, cool, I don't care. We have those two properties. I don't care about
the rest, right? Which may not be what you want. So if you actually want that behavior of like
getting that error that we saw on the previous slide, you have to explicitly type it. Right.
So this kind of makes sense. It has an extra prop. It breaks. But if you're just like really,
want to add this extra property, you can literally just spread an object into this and then the type
here goes away. So that's literally the first WTF of Timescript. So this actually blew people's
minds. They're like, what? I didn't know you can do this. Honestly, like pragmatically, you probably
would never do this. Most people won't even... Except people do. Yeah, exactly, exactly.
So why does that work? You get somebody told the whole their own. Because I thought spreading like
TypeScript was able to trace the types through and the properties that you get. Why does that work?
I don't know the technical reasons, but I just don't think TypeScript can actually follow it, right?
I just think it just sees the first two properties, and then it just can't, I think it's like the same scenario where you extract it like this.
It doesn't know what's in there, and it kind of just treats everything in this object as an excess property at this point.
So I don't know, I don't know, like the actual technical reasons if you go into the compiler.
I'm sure some, like, team member can tell you why.
I just know if you do this, it makes it go away.
And I've seen this actually like in some like, so why wouldn't you just use a TS ignore at this point?
or an as person.
Well, TAS ignore just opens up a whole other thing of like candle worms.
Usually what people do is they'll opt for this.
They just want to explicitly talk it.
Because at the end of the day, as long as you have first name and age,
it doesn't actually matter.
Like your code is only typically going to look for those two properties unless you have
like some validator that's actually like enumerating the properties of the object.
It could actually break stuff.
But more times than not, this would usually be fine.
So this.
Trash, I got a question.
Yeah, yeah, yeah.
If this behavior didn't exist, wouldn't like everybody's strategy for passing around properties and react just like completely get demolished?
Isn't this what everybody does is like you've got some big object with a bunch of context?
Everybody just spreads it all the way down, right?
Do they not?
Yeah, that's like a whole other conversation of just like dumping bags into like other functions and you have no idea what's in there.
This is a problem.
I don't know like the exact React types, but I know the practice of just like taking a bag of.
anything and just passing it down is a thing.
Yeah, you're just like, we just pass.
We just keep up as it down.
Somebody uses it.
It's actually a real big problem, and I've experienced this pain firsthand, and I actually
hate it.
I don't think it actually at the type level catches it, because I've been in code bases where
we're just like, I don't even know what's in here, but I'm just going to spread it.
I'm just going to spread it down and drill it to the core of the earth because I have
no idea.
So, yeah, I don't know at the type level.
I'm not too familiar with like the React types in depth, but potentially, right?
Casey, just so you know, when someone spreads, it's, it's,
It's actually taking that object and copying it key and value in, not doing a deep copy, just a shallow copy of key and value, which means that every time React renders, every single component recopies the same object through all the way down.
So if you have thousands of components, you create thousands of copies of objects every single time.
Just so you understand, because I feel like this would be kind of like a fun, like illuminating fact for you.
Actually, trashed, I mean, this is going to be one of those things because since we're all actually interested in this, like, this.
This is going to be like one slide per podcast.
No, this is perfectly fine.
And six weeks from now, we'll have gotten through the presentation.
Sorry, just for my benefit.
So can you go back to the thing where you called the function and you got the error in the first place?
Sorry.
I'm not supposed to go backwards in this one.
Oh, I'm sorry.
Okay.
Well, I just, I was curious.
So I'm just wondering, compiler-wise, why does it see a difference between these two things?
Because to me, when I look at that, I'm like, okay, what that would normally do is first it would evaluate to an object.
That object should be the same as the person object you were creating before, because that's exactly the same members, in exactly the same order, written exactly the same way.
So there's just, there's a special case in TypeScript for if you're constructing an object only inside the call to a function.
And in that case, it will check.
Yeah, so the compiler will treat in line.
objects this way, but outside it won't do it.
I don't know the exact like specifications on why, but this is like the behavior that everyone
has kind of been like accustomed to.
So that kind of suggests this is very intentional, right?
Because that means they probably special case this and said, oh, like if this is a, if this
object has a property that isn't used because the only place that it actually would go is
someplace where it's not used will give you an error about that.
But if you have a regular object and you could pass it to a function that would work
we're going to make that work.
So that it kind of feels like that must be intentional.
It's called duct typing, right?
Yeah, exactly.
They have a term for that, right?
Exactly.
If you think about how just JavaScript works, it doesn't care about any of this.
You can pass anything, right?
Like you said with duct typing.
I mean, like you said with duck typing, if it just has these properties, it must be a duck.
And this is not necessarily a bad thing, T.
Like, duck typing is a feature, right?
It's a thing that.
And so, you know, when you're making a language like they were with TypeScript,
they have to decide what's the default behavior.
And maybe, like, their thing was like, okay, we're going to decide the default behavior is duct typing.
And if we detect somewhere where it's clear that you probably didn't actually want that, then we'll error.
Like, in this case, because there's no way extra prop can be used here.
It's only going towards a function that, right?
So, you know, I'll be honest.
I know people have probably expected me to think that everything sucks.
But looking at this, I'm like, that sounds like an adult design this.
Just completely honest, that seems like an adult design this to me.
Yeah, I mean, like, just imagine just trying to type a dynamic language like
JavaScript.
Like, it's never going to be perfect, right?
So you can always get as close as you want.
Yeah.
And then you kind of make these arbitrary decisions on like how you want like this experience to be.
So Casey's thumbs up so far.
Sorry, guys.
I'm up.
I'm thumbs up so far.
Can I ask Casey, do you think it's, is it, do you like that?
Because I agree, I like this one where it says, yo, you pass an extra prop, you shouldn't do this.
But do you.
like being able to do the next phase where it's like, oh, well, I just put, I just have an object
and I pass it and has extra properties. Because that, like, yes. Okay, all right, cool. Just making
sure. Like I said, it's, so duct, I actually like duct typing if I ask for it, right? So, like,
I would prefer that more languages have the ability to do duct. Like, for example, you can think
of C++ plus templates as supporting duct typing. Because if you pass something as pass, I mean,
if you instantiate a template and you have a parameter that has the necessary things,
but also a bunch of other things, it will work, right?
Because what it's doing is it's going to create code that works for anything that supports
that feature at a minimum.
It's not going to say you can't have these other things.
And to me, that's actually better because it gets me more code leverage.
It means I can use more functions on more types, as opposed to the opposite,
which is when you try to do the object-oriented, like very procrastin, like type hierarchy
thing and make it's like we only everything has to be the minimum set and functions can only work
on exactly the things we said right that really handcuffs you right so to me duct typing is great
would i have wanted it in an ideal world perhaps to be more explicit like i say if i want
duct type or not maybe but again in the context of a javascript front end which is what
typescript is i think this is a reasonable choice for a language designer to make because most
of the existing JavaScript ecosystem
is being duct type by default.
So if you make it really onerous
and annoying for the person writing
the code to costly be saying, duct type,
duck type, duct type, duct type, duct type, that's
going to be annoying. So again,
seems like adult designed to me.
So I'm thumbs up on everything you showed so far.
Can I ask a quick question? This is just a general question.
Is there a specific difference, in my head,
the duct typing versus structural typing.
And structural typing is about the properties
and the available functions,
whereas duck typing is only about the functions.
Is there like a difference?
I'm not actually sure if there's actually a difference,
or is duck typing like the local or the colloquial version of structural typing?
I don't think I know the term structural typing,
but duck typing literally just comes from the phrase
if it looks like a duck and quacks like a duck that it's a duck.
So literally it's just like if this thing can do what I need,
whether that's member functions or having particular data items or whatever it is,
then I consider it to be the type I needed for all intents and purposes.
And I'm not going to inquire further to see whether it actually is a exact copy of that type.
I'm just going to say, does it have similar things to that type that will work, right?
I don't know what is structural types?
So I don't know that term.
So structural type, I just think about Legos, right?
So if they have the same shape, they're probably the same thing, right?
So like in this case...
They're the same, basically.
Like, structural subtyping, I think, is the functional programmer.
way to say duct typing. That's where I was having this impedance mismatches. I knew they
in my head they're the same thing, but I'm like, what's the difference? There has to be a
difference, but okay. Well, if it comes from functional programming, they're definitely not the
same thing. And somebody will be in the comments going, I can't believe they called structural
typing, duck typing. And then 20 paragraphs later, there will be this minor, tiny little
difference that no one cares about. But yes, they probably are the same then. So we'll get you
guys in the next episode. We'll have the answer solved. I'm not going to look it up myself. I will
just read through the comments after this video. Thanks guys. All right. Trash, go on.
All right. So 20 minutes later, we got past the first section. We'll see how far we get
of this. This is actually, okay, let's keep going. Okay, Enums. I'm assuming everyone is it.
Enums. Okay, this is an interesting example. I wouldn't say it's the most interesting one,
but I think it's pretty, pretty cool. So let's look at Enums, colors. They just have string
values. So green orange red, nothing particularly interesting here. But we want to use this
enum in a function. So we'll just have some placeholder function is the best color. Its parameter is
going to be of type colors. That makes sense. Right. Oh, sorry. I'm not you're asking.
Oh, green is the best color. I was like, how did you know? So if we call it like this,
colors dot green, this makes sense. I just shouldn't look any, it shouldn't look surprising to anybody
that's used type script or just coding in general, I feel like. But if you call it as its actual value,
it's going to break.
So if you see here,
we're calling it with a string value green,
but if I console log colors dot green,
it fails,
even though the values match, right?
And this isn't the biggest deal.
The biggest annoyance I think I experienced personally
in other developers is when I call this function,
I now have to import this enum
just so I can call it.
Even though if I know the values,
I can't just call it with green orange red.
So with auto importing,
not the biggest deal.
But where this is interesting
is going to be in my next example.
So right now,
any questions like this is all.
Is this like, does that all make sense?
I can't see you.
Good so far.
Okay.
Good so far, trash.
All right.
So let's talk about enums with number values.
So we're going to have like pending decline approved 012.
We're going to call it again, just like we did in the previous example.
Stats dot decline, you know, makes sense.
Makes sense.
It makes perfect sense.
But if I call it with this corresponding value, we don't get that type error.
So it looks like.
I love C.
So yeah, it works here, but it doesn't work in the street.
in the string case.
What happens if you passed three?
I don't actually know.
I'll type it up right now.
I'm pretty sure it's going to work
because there's just going to be a type number.
But I can type it out real quick.
I'm doing it right now.
Do it real quick.
Oh, boy.
I almost...
Right now it says argument of type three
is not assignable to parameter type status.
It's probably going to be a type area.
Yeah, it doesn't allow three.
So it does actually look for the numbers.
Interesting.
So it's like half.
Half C.
Half C, exactly.
So this one's like interesting.
Like it's not something I don't think anyone's going to actually encounter like day to day.
But it was like an interesting case on like its behavior.
So I included it.
That feels like it must be a bug unless they couldn't have intended that.
There's reasons that I don't remember.
Oh.
I don't remember.
But there are reasons.
But reasons that you don't ever probably care about.
But now I'm going to look.
I got to remember.
Like when I was making this talk, I was like, I learned.
why it's doing it and then I completely just purged it from my memory.
Interesting.
Yeah, I'd be interested to know why.
Someone is trying to say that it's a difference between references versus direct values,
but they don't understand that V8 at least operates on heaps or a SMEs, small, small numbers.
And so that means if you have a number below 2 to the 32, it's actually inlined inside the reference.
So it looks like a heap item, but it actually is a tag to say, hey, this is actually an inline number.
So I wonder if this works with numbers above 2 to the, or yeah, 2 to the 32.
Does that break the whole reference?
Because then it becomes a boxed value
and it's no longer a small number.
Potentially, I know there's actually a limitation
on what numbers you can use as well as value.
But TypeScript is compiled, so it wouldn't care.
It wasn't matter what the runtime engine uses, right?
I think there's also stuff built in into the compiler
that will like...
I'm serious, right?
Like, this is a compile time error, right?
Like, this is not a runtime error, it's a compile time error, right?
Yeah, yeah, yeah.
So it doesn't matter.
Like, what VA does?
is irrelevant. This is clearly something
that the compiler must know, because
it knows that the definition of validate status
uses type status, which means it must
be visible. So it knows that
you're passing one, which is
a constant, a numerical constant,
to something that requires status.
So somebody, like somebody
in the compiler said
that was okay. They must
have had a reason for that, I think,
because that would be a
pretty straightforward bug to fix if it was accidental.
So I really would
would like to know what the rationale was because those two things are known to the compiler,
and it's choosing to let you do that and not let you do the green case. Why?
Yeah, I'm pretty sure it's like an explicit from when I'm trying to recall what I remember,
but I think there's like an explicit decision here.
Yeah.
We'll find out.
We'll find out, though.
It's easy to figure.
It's easy to find out for sure.
All right, that's point number two in the YouTube comments we'll be looking for.
Yeah.
We'll read it.
Yeah.
Yeah.
Can someone leave a YouTube comment and let us know why?
Yeah, hook a Casey up with that.
Also, I do, I do want to say that type script doesn't know what is or
is not a reference in the sense that it would actually have to do type checking of the thing to be like you're a string you're definitely a reference oh you're a number how big is the number to see if it's a reference or not right you don't actually get that access it's not surface to you in javascript so it can't be based on that it's based on someone made the intentional design decision that numbers can be raw dogged and strings cannot yeah and they are checking like they they like three didn't work so it's not like it's just like i don't know we're just going to push numbers like no in the compiler it knows so this was intentional and i'd love to know why yeah
be interested to know. There might be a very good reason. Like, oh, yeah, because of blah.
Like I said, I, I want to say, I want to say that I remember the dude who made the
TypeScript compiler in the first place, or one of the main dudes. I want to say that he was a friend
of Dave Stafford's that I had lunch with them way back in the day. And this dude is baller.
He wrote, like, I think he's the guy who wrote the original Microsoft Java, like, before V8,
before Chrome for any of these things,
the original JavaScript
like runtime engine
for like IE when it first got that.
And it was pretty good.
It was better than everyone else.
It was like way better than like Netscapes or whatever, right?
So I'm not coming into this with a mentality
that this was just like a bunch of toddlers
and diapers running around going like,
woohoo, right?
I think like I'm going to give them the benefit of the doubt
for quite some time.
But I feel like there's an explanation here.
I'd love to know what it was.
Anyway.
I'm going to stake out my guess.
And then I'll,
well, that's also the best way to get a YouTube comment because they'll say,
actually, TJ's an idiot.
Here's what it is.
So here's my guess.
And I actually think it has something possibly to do with what you're saying,
Prime, is I do wonder if there's a few comparisons or operations that would work for
numbers to check a quality that don't work for strings, where if you're not holding a reference
to the original string, right, like where you do green, if there's like some comparison that
checks like the string green versus another string green, are there some operations that get you
different results? That's my guess. Maps would be the only ones. Yeah, true. I think you can use
map and set actually use references potentially. I'd actually have to double check that to see,
does it use references to string green or is it actually just literally string green? That's,
that's, I don't know, but I don't think that anywhere that you can do that with Enum. So I'd be very,
it would be a surprise to me to know that you can do reference based in anything in JavaScript,
other than potentially maps.
That's my guess.
That's a good guess.
It's a really good guess.
So let me show you the alternative to enums
that people started doing instead.
Oh, oh no.
Are we going off script here?
Are we going off script?
No, can you see my screen?
Oh, he's on script.
Holy cow, nice, trash.
Well, I didn't do this during my talk,
but I'm going to do it here.
Yeah, I love this.
So people stop doing enums
and they favor this stuff right here.
So this is actually like you actually defining,
this is JavaScript at the,
this point, but then you add this as const here, which is kind of like object dot freeze,
kind of freezing the object, but at the type level. So instead of doing enums, people actually
do this, convert that into a type. And then this is kind of getting into the weeds, like,
you're probably just going to be like, what the hell is happening? But the experience now is,
I don't have to import an enum. I get like this, I can now see all the values that are valid.
Right.
So this is what people like to do because at the end of the day, the enums actually turn into runtime code,
which is actually one of the things that, like, I believe the TypeScript team kind of regrets is using Enums.
Like there's actually like a rule in TypeScript now.
If you're doing TypeScript in Node to actually not allow you to use Enums because they're not strictly at the type level.
They actually have runtime implications.
So people actually start doing this instead of doing Enums.
I'm not going to explain it, but I just kind of wanted to show you like this is an alternative.
Explain it, Trash.
explain it. This is your chance. So again, I know, I know. So here we're basically, this is where
we intersect. Okay, let me back up. So TypeScript, which people always like mess this up, is
strictly at the type level and JavaScript is at the runtime level. Like they don't intersect.
The place where they can actually kind of intersect is this line right here is where you do
type of foo where this is actually referencing some runtime code, right? This is actually,
like if you log this in your browser, it would give you these things. But,
Now you're converting this to a type via type of, and now you're, now you're officially in the type world, right?
So we've officially turned this into bar, and now we can see the type here, like, it's read only failed.
And we see it's read only because this ask cons, because it behaves of, like, freezing something at the type level.
So what we do now is we can get all the keys.
It's an object, right?
So if we get the keys, we now see the keys.
Dude, that's my dog's favorite type.
What?
Barkies.
Oh my gosh, stop
You are the worst
Wait, is that an actual brand?
No, Barky
I thought it was like a brand like chewy or something
Okay, okay, okay
Trash, the intersection of dad jokes and programming
Is T.J
I only wanted to go line by line through this
Because I had that joke as soon as I saw you bring this out
Oh my God, I am not been allowed
let this joke pass.
I'm going to get a chance to drop this joke.
Let me let me fast forward through this because you just waste.
Hold on.
I got a question for you.
I got a question for you.
You can't use this approach with like type of and Kiev on an object that you don't know
the shape of, right?
So like if you read something from a file, you can't just be like type of thing.
You have to first give it the shape and then you can type of it.
There's also another word called infer.
And that's typically what you would use to do.
So say you're using a third party library and they didn't export type.
you would have to use the infer keyword to turn like someone.
Okay, and that does some sort of like forward-looking or something to determine all the keys used on it and then says it's this thing.
Yeah.
So like, yeah, so that would be how you would do that.
It won't work like in every case.
There's like quirks around that, but that's how you would like kind of do that.
But anyways, like this is the most interesting part here is like this is how you intersect the worlds of runtime code and type and then type script code.
is via this typo because now after this line we're strictly can you guys hear the vacuum
yes oh yeah but it's it's is dax at your house do you have dax over i got dax's people over
right now oh no did it if i keep talking does it go away like if i just keep saying hey no but just
keep talking and we'll pretend like it's fine it's fine okay anyways well i just want to show you this
real quick so again here's the keys of that structure that object like structure at the top
fail and success if we want the values we would just do it like this it looks like
JavaScript, to be honest, right?
Like, this is kind of like how you access objects with their keys, right?
And you get their values.
So now, if we did bar keys based on Teague's joke, right?
We now see we get the auto-complete of this, failed in success.
And if we do bar values, we can now see we get the auto-complete.
So this is like an alternative way to enums, which people actually like doing instead.
Like I said, the other quirk is like with now node.
They have like this new like strip.
I forget the name of it.
But basically they don't want you using Enums and Node in TypeScript.
I feel like I missed something here just so that we make sure that we get through a maximum of two slides per hour.
So I see what you're doing here or whatever this.
I understand the example that you showed here.
But what I must have missed somewhere in between those is why.
So what was wrong with the Enum version?
And also, so like, at least for me,
what I would have wanted as a programmer when I was initially creating an enum is I wanted to see
this is this is probably because I'm coming from a background of like programming languages with
enums that maybe work a little differently but what I wanted to see was X parentheses success
no quotes around it because to me as a programmer that lets me know oh this isn't a string
it's an enumerant so this actually looks more confusing for to the user to me so this
looks worse than the enum case to me, and yet you're saying people go out of their way to do it this
way. Why? What was wrong with the enum version that makes them want to do this? I think this is more up for,
I think it's kind of subjective, in my opinion. So I'll tell you why I've seen people use it and why
sometimes I use it. One, because it's no mystery on what's actually being compiled to. So, like,
if you did an enum, you don't even know what that looks like at runtime, right? Because it's all compiled down.
So at least here, I can actually see what the Java's output is going to be.
So that's like one reason.
So I can see the runtime code next to my types.
It's not hidden behind the compiler.
Oh, like they don't want to see 0123 is what you're saying.
Yeah, yeah, yeah, yeah.
Okay.
Exactly.
And then also the other end thing where you have to actually import,
like if this was Enum Fu, I would have to do something like,
I'll do Vim mode, broken the playground.
Oh my goodness.
Let me refresh it.
I can't do this.
Oh, man, this thing sucks.
Okay, anyways.
So what you would normally do,
you would do like food dot failed right and you have to and you would have to like import that here
you could you would actually since you are familiar with the values you could just do something
like this and get everything right here so it's like that's more of a dx thing i don't think it's like
the biggest gain well um but i personally think people do it because of this and someone someone
in the comments can tell you otherwise okay but i think this is like the biggest win is actually
seeing what the runtime i'm starting to understand i i see i think kisi and you are talking about
two different things too, right?
Because Casey's used to cease out where they're actually, they're like identifiers.
Enumerations become identifiers for you to know about, whereas in this world you're actually having, it's not that.
It's like these ethereal things that you actually reference with strings.
Yeah, let me show you another big foot.
TJ has a thing.
Oh, yeah, go ahead, go ahead.
Yeah, so, yeah, he can't see me.
So that's okay, Tresh.
I do think the CM Griffin, shout out CM Griffin in the chat.
And I think this is the other main reason is if you have to implement.
import, like you got to remember as well, Casey, right?
Like, people are not just shipping one blob of stuff.
Sometimes sometimes this makes for, like, they are shipping one blob.
They're shipping it to the front end.
So this could mean like, oh, you have an enum.
You want to use it in your code.
Okay, now I import this package.
And now all of a sudden I like, I'm going to balloon the size of my JS bundle.
Normally people like that kind of thing happening.
But like in the web world, it's kind of a net negative to balloon your bundle.
So they're trying not to have that.
So if you can just do a string directly, you can avoid like that scenario.
I'm sorry, I just say, like if I had to summarize my understanding of it now, just having only seen this much, I guess I would say the problem is that JavaScript, the runtime layer, is basically forcing you to do one of two things.
Either you can use some kind of constant that already exists in JavaScript, which is either going to have to be a string or a number, right?
and if you choose number,
then anyone who's calling this
who's not using TypeScript
is just going to see like three
has the parameter,
which is not what you want.
You wanted something that they could actually read.
So you have to pick string
because it's your only other option for a constant
unless you were going to literally import an object
whose references you could compare
which is what you don't want to do.
Is that a fairly reasonable summary
for like why it's like, okay,
because otherwise it would just all be numbers in the code
and people don't want that
because it can't be read in JavaScript.
So it doesn't allow you to do like pound defines or anything.
Correct.
So here's one more interesting thing is like,
so enums will automatically assign number values to them.
So like red and green 01.
So if you define your enums like that
and then in your runtime code you're depending on that order,
you could easily break all your code by rearranging your,
the order of your values.
Does that make sense?
Yeah, well, that's kind of normal for enums in general.
Yeah, so that's like a footgun that people don't understand.
So people like, if you actually need to depend on values, you need to explicitly define them.
Got it.
Otherwise, they get implicit values.
And if you're depending on those implicit values, you can usually just like mess up the logic.
Like they save something to disk and reread it.
And they think that because it's named the same thing, it will still be equivalent, but it's not because we change the order.
Because green would be zero.
Yeah.
But it was one before.
Got it.
So nothing I've seen like too much in practice.
like on my end professionally, but I can definitely see someone like doing that.
Got it.
Especially since a bunch of the functions for numbers will accept just a raw number, right?
We saw that earlier.
We saw that happen, yeah.
So it's like that won't give you an error.
That does seem like a recipe for disaster.
Right.
Love it.
Learning a lot today, loved Galactus.
Right. So here's another quirk that I think is very popular in the ecosystem that people like experience day to day.
So like if we use like a filter function of Boolean,
that's just like a fancy way of just filtering out falsie values.
So like in this array, we have one undefined three.
The falsie value is gonna be undefined here.
So at runtime, if we log this, we get one and three.
So by looking at that output, we would expect,
since it behaves this at runtime,
that it would give us a type of like a number array, right?
But what actually happens is we get number or undefined
because it's not able to narrow that.
So like the implication here is like there are workarounds
with like things like flat map and like type
typeguards. But even though you know for a fact that runtime, you're never going to get undefined,
you know how to like program defensively against this case because Typhscript will give you like
those like red squigglys because it's like there could be an undefined here. So that's an annoying
part. And this next thing, this one hit hard during the live, the live thing. So I had like this
thing here. This is Matt Pocock. He's like a big, he's like a big, a big person in the
typescript community. Dude, everyone laughs so hard. I was like, I was so happy. I was like, man,
before I hit the button, I was like, please let everyone.
left. But anyway, so this is Matt Pocock. He built a library called TS Reset. And it will basically
reset like these weird quirks that kind of go against your mental model. So if you use his library,
it's kind of like a CSS reset, but at the type level, we're on TypeScript. So it would actually
give you number array versus number undefined. And there's a much of other cases as library
accounts for. But like, like looking at this though, like this is kind of annoying and it's
like a common thing people work around in their in their code bases. So I don't know if
there's any comments here. Not like in like nothing too too crazy, but it is like really, really
annoying. It makes perfect sense. I mean, you know, because it's like unless you special case
filter and you basically say, all right, instead of treating filter as just a function that
operates on an array, I now have to treat it as a special case where I know that it happens to
remove things of a certain type from this array. Therefore, I should mutate the actual type of the
array that it returns to be different from the type of the array that it takes, which would be a
very unusual thing for a compiler to do. So it makes perfect sense why they did it this way.
I can also understand why people get confused when they do it.
I think the course, like the workarounds, like it just gets verbose, like having to do like a typeguard
or there's some oddities. It just kind of goes against your mental model. So the big thing is you can
actually throw a, you can throw a generic on filter, but Boolean is a constructor. So it doesn't
really understand that.
Boolean is also a function.
You can treat Boolean as a function.
And so it kind of blows up.
And Boolean just returns, you know, it returns the positive case.
So it has a really, it has this hard time mapping through things.
And so it looks like it's, it just, it can't do it.
So you can't use generics and a bullying thing because the constructor returns not
that.
And so filter kind of explodes.
You could imagine something even more ridiculous, right?
Like if just to illustrate how odd this kind of case is, right?
is like, let's suppose that you imagine something where you're like,
we have a typed array, and you're like, what type of stuff does this have in it?
It's like, oh, you know, like imagine you distinguish between integers and floating point values.
So numbers like 1, 2, 3, 3, 1, 3,000, right?
And you can create one of these, and it has a mixed set, so it's got 1 and 1.5 in it, right?
And then you have some mathematical function or something like this that goes through.
and rounds all the values.
Like, you wouldn't intuitively expect the compiler to now change the type of the thing that you were, right?
Because it's just something that happened to end up being true after some operations occurred on this thing.
And that's sort of what's happening here.
And, you know, when we're in C or C++, something like that was very rigidly typed.
And, like, everything has a specific storage type as well, as opposed to sort of this generic storage type like JavaScript uses.
then it's very hard to get these kind of corner cases
because look, if you had storage for a double,
then you're always going to think of that as a double
no matter what value it takes, right?
And so you don't expect it to be able to handle anything like this
because there's nothing like this that can happen.
But when you're in a very flexible language
where the type of things can kind of mutate all the time,
I feel like that creates real difficulties
for the compiler vendor who wants to try and make it appear
as if these things are more rigidly typed.
So this to me makes perfect sense.
And like I can totally see why people are confused.
But like, yeah, that's exactly what I would expect it to do too, is the weird behavior.
Because that's just like, yeah, what else do you do?
Except special case or like that dude did make libraries to try and like special case all the things that might trip you up and try to fix them like ad hoc basically, you know?
So this seems fine.
I'm down.
So Casey, something that might blow your mind is if you redo this, you put a generic on filter with numbers.
So it's like, hey, or return a type number and then pass in a function that checks for to not be undefined because the thing that got passed in was of type number and undefined or undefined as an array of one of those two possibilities.
And your filter function filters out specifically undefines.
It's able then to infer that the thing coming out is only numbers.
Okay, so it actually is pretty advanced.
It's like a lot of type checking in there.
Yeah.
So, you know, I mean, this is like I said, I just, I can totally.
totally see just like, look, there's probably a bunch of cases like that.
They caught some of them.
They didn't catch others.
You know, and so, yeah, so good on them for catching any, honestly, I think.
Yeah.
All right.
Ready for more.
Hold on.
Let me see.
Yeah.
We're moving so slow that I'm going to have to like pick another one.
Okay.
This one's interesting.
All right.
So at the type level, we're going to have empty braces versus object versus lowercase object.
And honestly, this literally.
trips up everybody. I don't think it's like as much of a common case anymore because people
just like explicitly type with like a name or something. But like when you look at this,
you kind of expect all three of those to be very similar, right? At least that's like how I
react when I see something like this. So if we look at how types could respond. So we'll just
have two variables here. We have foo and one with lowercase object, one with the empty braces.
So if we do the one on the left, this one is.
is going to, this one actually aligns with most people's mental models.
It's going to have an object in an array, right?
Those are both objects, but any primitive value will not be accepted.
You know, bullions, null and defined, strings.
So this makes sense.
But what's interesting, like if people are familiar with JavaScript,
empty braces represent an object, right?
You would, I think like most people would probably
think it would behave the same way.
But the interesting part is that it accepts literally everything
but null are undefined.
And that's not very intuitive when you kind of look at this.
I actually see a lot of code bases where people just say like something extends empty object,
when they actually mean they just want something to be like an object-like structure.
But what's happening is you're literally doing numbers, bullions, and objects, right?
So you end up allowing way more than you want.
So this is something that like this is actually a huge problem I've seen pretty much in every company I've ever been in
doing this kind of syntax.
So that's the interesting part.
Why isn't it just only, why isn't Foo just like only an empty object?
Is there some sort of like writing about why it, like, because if you did squirly brace,
but you put one key in it, then it's structurally that one key.
But once you remove it, it becomes everything.
Why isn't it just like it only allows for empty objects?
I don't know.
You're asking the wrong.
We have to look at the compiler code on that one.
I don't know.
I just know like what the like result is.
But yeah, this is kind of weird.
And I always found out to be a foot guy.
because like at face value
these all kind of look the same
but yeah
I don't know
I don't have to look at the compiler code
to see like exactly why
or maybe someone in the YouTube comments
can explain
who's on the type script team
could like explain that to us
this is like a perfect episode
to be manned in the comments
right right
what's weird about it to me
is more that it does
that it doesn't accept
null and undefined
because otherwise they'd be like
oh well maybe they just decided
that open
open brace close brace
is the shorthand
for I don't know what this thing is yet.
Like, it's just like, I'm not, I'm just like telling you that this variable will exist,
but I'm not going to say what it is yet, which, you know, we do in other languages, for sure.
And so, but the fact that you can't make it be null or undefined makes it seem like it's like,
okay, well, we do want to say something about it, but we just don't want to say that much.
And it's like, what is the use case for that exactly?
Like, why do we need that?
So I would be interested to hear, you know.
So we have a type for like the case you mentioned.
It's called type unknown.
It's a built-in type.
And that's like if you literally don't know what it is.
Yeah.
And then you basically have to like validate a runtime.
Yeah.
What it is because we just don't know.
We don't want you just be like accessing an object if it's not an object or something.
And this is definitely not that because you can't make it be null.
Like you can't decide that it's, oh, it's just not anything, right?
You know, so I don't really know.
This is pretty interesting.
I'll be interested to see the YouTube comment on that.
We did get one comment in chat, which I does seem at least flaws.
thanks Spion
669
null and undefined
throw when you attempt
property access
so that's what I was wondering
because I think you can do like
42.2. length right
or not length probably
but like 42.2 string
right or something like that
oh it's structurally it's an object
oh it's prototype is still an object
at the tippity top
yeah okay
right is that I can buy that
I don't know if that I can buy that
I don't know if that's right
but I can buy it
I don't buy it
I know I'm getting
Only worth what you'll pay for it, Trash.
It seems plausible.
Let me ask my man, Claude.
Let's see if you agrees.
Because I would have a squirrely brace.
Would be true.
So far, this one is the first one that really does seem to earn a WTF.
Legit.
Like, no, like, I don't know, man.
Like, if you see this in code, like, probably just be like, don't do this.
Okay, yeah.
Have, like, a good reasoning.
All right.
I don't know.
Okay, hold on.
Let me see.
Where we have?
I know we're getting short on time here.
I want to.
We're never short on time for you.
brother like take as much time as you want it's true it's true this is one thing I wanted to show
Casey so this isn't actually like your type stuff but this is the doom and type script that
my friend Dimitri did okay so this is what it took for him you're gonna see what I mean I'm
not sure are you familiar with this or no three he doesn't know he doesn't know you know okay
this is perfect trillion lines so with three and a half trillion lines of code which is
about 177 terabytes with 12 days of running said code and through a type checker and just
being like mentally ill, this is what you're going to get in TypeScript. You're going to get the
first frame of Doom rendered in TypeScript. So this is someone doing...
Types.
Yeah. Strictly through Types.
I see. So this is someone who basically said, look, is TypeScript's compiler Turing Complete?
And the answer was yes.
Exactly. Literally. Yeah, exactly.
And they were like...
The funny thing is at the render, someone was doing TypeScript is Turing Complete like a couple hours
before mine and we both referenced this.
I was like,
like felt to my knees.
I'm just kidding.
But I was just like, oh, man,
I'm a fraud.
I'm a fraud.
And last thing, last thing,
I kind of want to show you something else after this
is you can do math in TypeScript,
right?
So if we see like Type Fu,
we passed in 1321,
we get 34 because math, right?
What I really wanted to show like the audience
was like, what does the resulting type look like?
And this is how you will,
accomplish this. So this is one of the cleaner implementations. Shout out Team Chong. I think he's
very popular in the open source community. So this is like, and it's going to scroll. So you go
like this and this is what you get if you want to implement some in TypeScript. Ideally, no one
does this. Like it's more just like pushing the limits of TypeScript seeing what is possible. Like
people have done like Global Soar and all this stuff. But it's just like crazy what you can do in the
type system. So like I got.
really addicted to type challenges, which is like basically lead code for types.
So like I absolutely just like I got tired of like, and everyone was like a big lead code
person, but I did do it because like if I was interviewing, but when I found out about like type
challenges where you can basically do type challenges like this, I got like addicted for like a
year and I just like went through like almost like not like all of them because they get like
pretty big brain, but I did like a hundred plus and I just got this is like what kind of
unlocked typescript for me. I was very much like a, I would say just the normal
application developer using TypeScript, you know, just doing type, like basic type declarations.
But then once I do it, started doing these like challenges, like I just completely unlock
like a different like avenue in my brain. I was able to just do like gnarly stuff at the type level.
And it was very helpful because I was helping with like libraries at work. I write libraries.
And doing this allows me to like give the quote unquote best, best developer experience to like my
consumers, right? You don't have to do stuff like exactly at this level, but you do like pretty
insane stuff that looks like spaghetti code and you can't read. So there's one thing that I think Casey's
going to appreciate, which I glossed over in this, and this is TypeScript performance. So sure,
you see all this code, but this is a very real scenario that I'm experiencing now as we speak.
Let's imagine we have a huge mono repo that has tons of, tons of contributors.
you have potentially some code gen.
You have a lot of like user generated types.
But now when you're doing your,
when you're working in the code base, right?
So this is me working in my code base.
Let's imagine this auto suggestion taking like 10 seconds to appear.
Oh.
Right?
Or let's imagine your build taking tens of like, you know,
like 15 plus minutes to just compile types, right?
And that's like a very real scenario.
And it's a scale not many people hit depending on like, I don't know, the work they're doing.
So being able to fix these performance bottlenecks is actually like black magic.
I think there's maybe, I'm just going to, this is just my general observation,
but I think there's maybe like I can count on my hands how many people actually know that are probably not on the actual core team.
That could actually help you debug your like your performance and try to.
to kind of help like build this. So what they give you is with like not really much documentation,
you get something like you get like a tracer and you get these graphs, which I'm sure familiar with.
So what I do is I run this and then I try my best to like figure out where the bottlenecks are.
But then even when I find out what they are, it's hard to tell why it's behaving this way.
Because when you're looking at types, you need to understand really in depth like, you know,
which types do lazy evaluation? Can I make this lazy? How does the cash?
at the type level. Like if I do this kind of type, is it going to cache to where, you know, I can reuse that and things are perform it. So you have to like really dig super deep into these like really obscure things and they're not documented. There's literally nothing really on the internet that can help you get through this. So this is like my biggest WTF right now because I asked for help on Twitter because I was like literally like at like my rope's end. And luckily some people like reached out to me. But even then it's just like I don't know. Maybe.
it's like kind of like guessing at its best.
So I'm hoping like eventually like with TSGO,
we're going to use that.
And that's going to hopefully speed up things because if it goes,
going to be significantly,
I think it's like a 10 times performance gain,
potentially depending on how your code is written, I guess,
or how your code base looks.
So this is like one of the big things I thought you would appreciate.
It's just like, I don't know how it's like debugging performance
in some of your ecosystems where it's kind of just black magic.
And you're kind of just like,
you just kind of just like,
around and find out because that's basically what I'm doing now.
Yeah.
It's like, well, this type looks gnarly.
Let me just like try different things and then run it again and see if it just got better by like 50 milliseconds.
And that's basically my life right now.
Well, a lot of times what we'll do in those kind of circumstances when we have like, it's like black box performance operations optimization right where you're like, okay, I need this thing to go faster.
But I have either no or very limited ability to get diagnostics from inside the box, right?
Like, so, you know, you've got a flame graph here, but I'm imagining, so I kind of implicit in what you were saying, or at least I assumed based on what you're saying, if I were to hover over the parts of this flame graph, it's going to tell me like what function the TypeScript compiler was in, but it's not going to tell me like on behalf of whom.
So it's not going to say like, it's because I was compiling this type or because I was compiling this dispatch, I'm assuming.
So what is going to happen is it's going to, this one's not showing you exactly, but it's going to give you some line numbers.
So you're actually going to have to look at the AST version of your TypeScript code to map like the position to like your actual code.
That's kind of handy though.
Yeah, yeah.
So like, so you have to basically look at the ASTs.
Even then it doesn't even map one to one usually from my experience.
Of course.
Yeah.
It couldn't, right?
Exactly.
But even then once you kind of figure out where it is, you have a whole other can of worms on like now you have to understand at the compiler level.
How these types are handled.
And why is this being slow versus like potentially another avenue?
And those things are like not really said at all that I realize doing now that I'm like in this world.
So this is like it's not something that many people can relate to until you have to do it if that makes sense.
And what size are we talking about here in terms of like, I mean, because I'm guessing there is basically an interplay here.
There's the size of the code base.
But then there's also like the level of complexity of type things you're doing.
So I'm imagining that thing you showed before that was using like a number of parameters cracker at the bottom.
I've seen those they're used in C and C++S as well to make macros that take different numbers of arguments, for example.
So I'm guessing that also it depends on how much of that kind of stuff you're doing.
Like if the compiler is having to churn through lots of transformations that you've told it to do, like using that kind of type metaprogramming, basically, I'll call it for now.
So is it basically an interplay of those two things?
Like how much type metaprogramming do you have?
Plus, how big is the code base?
So, like, how much are we asking it to do?
There's, like, some weird fuzzy function of, like,
if you hit too much of both of those,
then you're in no man's land or...
Exactly.
So, like, in my experience, what I've learned so far is,
our problem is kind of unique.
It's just literally the sheer number of explicit types
because we use code gen to generate some stuff.
Okay.
But also, it's also, like you said,
like inference like if we're having type script try to figure it out when you do that it really bogs down
type script so what you typically have to do is be very explicit with your types and just be like this is
what it outputs you don't need to figure it on your figure it out on your own let me explicitly type the
return type of this function and type script like okay now I don't have to go in and analyze everything
oh this is going to output like an object with like these objects oh let me just give you an
explicit type and you have to kind of like cast you do like as and stuff and that kind of helps as well
but it's it's kind of just like yeah there's like a lot of different quirks you can do so type script
has the functional programming the classic functional programming compiler problem where there's it's
like a bad exponential if because you're since you don't ever have to say what the types are of functions
it's just implied based on what the function does then if a function calls another function calls another
function calls another function and so on. You end up with this permutation of it having to like
solve the whole chain and see like what would it actually be. TypeScript has this as well.
So yeah. Okay. But so check this out. Here's like another thing even outside of this. So let's just
assume I have a type file that's like 60 megs big. Like that's huge right. Okay. Okay.
TypeScript types. Yeah. What you now need to do is you now need to slice and dice this file
and have it only pertain to like the specific pieces of the code base that actually care about it.
So right now, especially most people in chat probably, they probably have just like one big like file or they have some files and they kind of just build their whole project at one time.
But when you hit this certain kind of scale, you kind of have to slice and dice your project to only care about specific things.
So you're not.
So you can like, you'll basically have like a bunch of different processes that take like five seconds versus one process.
that takes like 10 minutes or something.
I see.
That makes sense.
Okay.
So like in the thing with TypeScript,
it basically like will statically just crawl your imports and look at every file.
So like if you had one file that was importing like 10 files,
TypeScript, the compiler will go and go span out to those 10 files.
And then those 10 files are most definitely importing something.
Right.
So then you just end up with this huge problem.
So you have to be very like surgical with like how you're splitting everything.
up because you don't want that to like leak in places as I need to. So like in my, so right now I'm
kind of in hell because we have such a big code base. I have to figure out where all these little
spider webs kind of span out to and kind of try to keep them in their lane. So we're kind of hoping like
TSGO is going to save us in some aspects. But at the same time like what happens is when you hit
this scale, you have to be very intentional with how you format your code base. Because most people are
just like, glab the whole thing. Let's go. Bill takes five seconds because my code base is like,
Maybe 50 files, but yeah, not the case for me.
And I'm, like, feeling that pain so hard.
You guys reinvented, the TypeScript World has reinvented the bad C++ compilation situation
where you end up having to build all these modules.
Oh, my God.
This is crazy.
I mean, very few projects to get this big.
So, like, to be fair, I mean, Netflix is one of the very few companies that are operating on this level.
Do the LSP still crash pretty regularly?
because I could get T.S. server to work for like a year straight.
And I was just like, forget this.
I got a slide.
I skipped over a slide, but it was like,
is this a TypeScript error or is my server dead?
Let's just restart.
Let's just restart just to be sure.
Like, I'm at the point where like, I'm just like,
is that a error?
Let's just, let's give that boy, I'll restart and see what happens.
And that's like the classic TypeScript experience, to be honest.
Especially like when you, like, get in the bigger codebases.
But, man, it is rough.
Question, is there like a practical kind of set of tools you can give developers to help this problem not scale as fast?
Like, I hate ESLint, generally speaking.
Can you throw in some T.S. Lint that's like, hey, you didn't explicitly type your return function.
You must return.
Yeah.
So there's a typescript.
Yeah.
Has there been a change where people are actually doing that at Netflix?
Or is it still kind of like you don't have to do that?
I don't think you do it until it's a problem.
I think that's kind of.
Yeah, but I think, I mean, that's not really unique to us.
It's like everybody, right?
It's not a problem until it is.
You ignore it and then, and then once it's a thing, you're like, oh, man, I don't mess up.
But I've definitely like-
That's a very human experience, yes.
Yeah, yeah, exactly.
So, like, I'm kind of like slowly trying to tell people what we're experiencing, like,
you know, just be careful.
Be careful out there.
Yeah.
It sucks because, like, like you said, like Netflix has such a unique scale that we now have to
just care about things that no one cares about.
It's like,
ugh.
But,
you know,
that's,
that's how,
that's how it goes sometimes.
But yeah,
that's all I got.
That's all I got.
Thanks,
trash.
I mean,
we weren't,
like,
on the script as my talk,
but I do.
So,
just to,
like,
backtrack to my talk,
like,
this was my first big conference talk,
and it was at the end of the conference.
So it was, like,
three-day conference.
I was at the last day,
almost at the very end.
And I was,
like,
terrified no one was going to come.
But,
turned out dude i had like a packed out room people were laughing on my jokes i felt like so relieved
but beforehand i was like doing stretches on stage just trying to calm down
i like i tried to like meditate i was like just sat there took like ted deep breaths i was like
did you did you go to the bathroom stall and stand really big increased testosterone do all the
power stances i drink like 50 bottles of water on stage before my talk and i had to pee so mad
no classic talk mistake one thing one thing
I'll say is like I used to like this was probably like my first conference that was like multi-day.
Like I've been to React Miami that I don't think that's as big as like the one I went to.
But like the I don't know like the support.
I never felt like the support from like speaking at a conference before because usually I did like virtual ones because of COVID.
And man, it was like pretty eye-opening.
Like you know, some of the people you see online, you know, very super nice in person.
Like I just changed my perspective on the whole thing of just like the community like of like,
conference speakers and people that tend to go to conferences regularly.
Kind of just like changed my mind.
Like not really about those people specifically, but more of like the community around it.
So it was pretty nice.
I don't know if I'll ever do it again because it was very stressful.
Like this was actually pretty stressful for me to do on this podcast.
But, uh, you crushed it trash.
Yeah, it was nice because there was a lot of back and forth banter.
So I made it like all worth it.
I made it all worth it.
I love that.
I actually think this was, I actually think this was, I actually think
This was really awesome.
This is one of those things where, unless if you used it,
you just don't know about any of them.
And I think a lot of people are actually pretty surprised about what's done.
I'm actually, I am going to say you didn't bring up my most hated feature of them all,
which is.
Oh, not this one again, dude.
Literally, just go back and watch the stream vods.
If you randomly go inside of it, you're probably running about this.
You can have you, so you know how you did kind of briefly kind of get close to it?
You know how you had number and or undefined?
Yeah, yeah.
Let me switch my camera.
No, you're on full mode right now.
You're full screened.
We're not changing it.
Oh, big trash.
Big trash.
You know how you can have like a number or undefined or you can do number or string
that's an array?
Oh, I've seen it.
I've seen you talk about this before.
Yeah, you can pass that into a function that only accepts string arrays.
Yeah, yeah, yeah.
I saw you talk about that.
Pull it up on the TypeScript playground thing and we'll show Casey.
Yeah, pull it up here.
I can send you a code example if you need me to.
This just blows.
Just give me some pasta.
Yeah, yeah, yeah, here.
I find this to be such a funny thing.
Prime's cooking the pasta.
He's boiling the water.
He's in the kitchen.
He's cooking up some taste of pasta.
Salt the taste.
Why are you sure your screen?
Just because I don't have it set up, and so we're not going to do it.
All right.
Trash, this is your presentation.
Even if it's Prime's example, okay.
I don't remember the example exactly, but I remember watching a stream where he was like,
what is this?
And I feel like he's brought it up like 10 times.
That's what I'm saying, Tremont.
That's exactly what I'm saying.
I've run it to 100 times.
If he's already a type script, he brings it up, literally.
Yeah, he's like, oh, are we talking things I hate about type script?
Oh, I've got one in the back pocket.
If they fix this in the compiler, Prime is done for.
Right, that's the end of his streaming career.
That's it.
It's like, there's no go-to anymore.
There's no go-to.
They can't fix this because it's not a thing.
Right.
Oh, that's what you think.
It's, uh, I ran into this with logging.
So when I was doing a bunch of logs.
logging, this is what got me,
was during a logging day working through a bunch
of logging, and then
it just, I just got, I just got really
sad, you know?
Here you go, trash. I'm not sure how to
send this to you. So I'm going to send it to you
via Twitter DMs.
Whoa.
I don't know, are we friends?
Oh, we are friends. We are friends.
Make a guest. I know, but then I have to go to gisting
and then I have places where we're talking with them.
You can also just post in the stand-up.
Yeah, I'll post in the stand-up.
There we go
My Discord is updating
It's going to take forever
They're compiling types
Also remind me
To remind me to close Discord before I share my screen
I am notorious for leaking everything
Inexam
I am too
Probably was watching this
They probably just updated all their type script
To fix the problems you were saying
So they had to update
We're on it
They're watching it right now
Just like really angry
Yeah that's what I'm saying
They're like oh my goodness he's so right about enums
Get those out of here
Atchastastast
Satisfs
So long enums
And the worst part is that when you first start off on TypeScript,
your first thought is,
enums are the right choice because this is what I want.
And it takes you forever to discover you've been using them wrong.
I feel like that is by far one of the biggest WTFs of all time.
All right.
What are we looking at here?
Actually, don't remember.
Okay, just look at it.
What is food?
String array.
Okay, what does Barr take in?
A number or string.
Yeah, and what do you pass into Barr?
A string array.
And what does Barr do to the ray?
pushes a number.
Oh, that's right.
Yeah, totally.
It could screw you.
Trash,
show us the type of bar afterwards.
Yeah, what's the,
what's the type,
yeah, what's the type of,
or what's the type of foo afterwards?
I mean, the result of bar foo,
you know?
Well, you don't assign anything.
Oh, shoot.
Right, so it's still just string array
the whole time.
Yeah, it's just string array.
And that, yeah, well,
food does, it doesn't return anything.
Oh, sorry.
Yeah, yeah, you can just look at food, though.
Like, what is foo?
Food should be a set of strings,
then you afterwards could have a function that, say, calls contains or includes or whatever it is, on the array.
And the array now has something that doesn't contain that function and will explode and will fly under the radar.
So this has happened to you?
Yeah, yeah, it happened to me on Netflix when doing the gaming stuff for logging.
Because we had such a crazy logging thing that if you look on my GitHub, there's a project called Undefined, where I take a, I take like one million query results from Mongo.
This is back when we had Mongo running all gaming.
it took a million query results from Mongo,
converted all of those into types,
and then figured out all the unions to reduce all the types.
We had so many different types that some of the types were kind of goofy.
So you had to like,
you knew you were going to be these collection of various unions.
And so I'd do something to the union
or have functions that take in subsets of the unions,
not realizing it was actually even a stricter subset.
And then, you know, it was just very, very difficult.
And obviously this was a problem of my own.
Are you just, are you just,
did you end up just programming defensively?
Everything, yeah, yeah, you just have that.
You just had to be...
Can I ask a remedial question here?
Yeah.
What is Const doing there?
Because you're pushing something onto it, so it's obviously not Const.
Casey, you just hit the funniest argument of all time in JavaScript.
Welcome to JavaScript, buddy.
Yeah.
Yeah, that's why I said remedial, because I'm like, I'm sure this is something that's come up before,
but like, what's the deal?
You just can't change, like, it's reference, but you can change.
you can mutate it directly.
So is there any way,
is there any actual, like saying that the contents are cons,
or can you only cons just the reference?
At the JavaScript level.
In JavaScript, if you want to be like purely frozen,
you can do object.offrease,
and if you want to do at the type level,
you can do like ask cons.
Can you do object.
Do freeze on an array?
Pretty sure.
I haven't used object.
d. freeze in like forever.
And that would mean that now it would be,
be an error. If anyone tries to push, it'll just be like you can. There you go. Yeah.
But you can still push a frozen array into a function that mutates it.
Oh, I guess not.
Wait, does this return something? I haven't used freezing a mall. Does it actually return something?
It returns a new one. Yeah, it turns a read only one. There you go.
Okay, so A is the thing we can't push. There you go. Yeah, can you pass A in a bar?
Like, what's the error? What does the error say? Just so I can see.
does not exist on read only string can you push a into bar or can you put a into bar i can't remember
if that's a thing you can do can i put can you call bar with a i can't no just call a or call bar
with a okay nice nice okay so the read only does go that direction perfect okay so you can
you can't it's just it's not that particular it's that not that particular syntax but
that works there is a question
That works.
WDF.
That works.
I don't know.
I actually didn't think that was going to work, but.
Well, hold on, trash.
Was the LSP broken or was it an actual error?
Wrote it back.
Run it back.
I know, exactly.
Let me refresh the browser.
I don't actually know.
Oh, dude, we just lost it.
Okay, it's gone forever.
I'm not typing that again.
So, can I ask another question here?
So I'm assuming that this is not really, like,
I'm assuming that this example is...
It's rare.
Well, I was going to say it doesn't even need to have as many moving parts, right?
Because, like, maybe it does.
So before you had a way of typing things, like, so can you put number or string up in the original definition?
So it's const-fou colon number or string, and then the thing as well.
So you want me to do this, right?
No, I was just curious.
So we can say whichever types we want.
So, okay, so my question is, no, I'm sorry, you don't have to, no, you don't have to do that.
Oh, okay.
So, would the push work even before you passed it to a thing?
So if you said const food string and you pushed three, just with it in full sight of the fact that it was only supposed to be strings, what would happen?
So you're saying, can I do foo push one?
Yeah.
Oh, that should definitely work.
That should definitely break.
Yeah.
Very interesting.
Very interesting.
All right.
Yeah, the problem is...
It just can't narrow it down.
Yeah.
It's like rectangles and squares, right?
Foo's a square, number bar takes in a rectangle.
This, to me, seems like a legit compiler bug.
It's not.
To me, because...
But it does seem like one, because in this case, if what we're basically saying...
Because in order for this to make any sense logically, right,
what we're saying here is that the ore is...
not a type definition, meaning it's not saying that I only accept number or string types,
which is A, probably what it should have been based on how it's compiling.
So it's compiling this as if what you said was, I will take only things that support number
and string, not either or, but, you know, it can support both.
But then what it's doing is it's actually accepting either.
So it's compiling as if it's and and accepting as if it's or.
That just seems like a straight up bug.
So I'd love to see someone justify that because I don't buy that.
There's two ways you could have made this work.
One is the first way, which is you say that's an actual requirement.
So if you tried to pass the string one, it would just error and say, no, I only accept number or string types.
And the other one would be when you actually make the call, if it actually was supposed to be more like a template where it says, I'll accept either or of these, then it should have.
done a compilation error check on that function as if it had just said type of string,
which it definitely didn't do because it would have given you an error on the push.
Right?
So I'm saying, I'm calling this a bug.
This, this is a bug.
YouTube comments, I will fight you next time on this.
This is a bug.
You should, you should have picked one or the other to do.
And if you did neither, then it's a bug.
Hey, guess what, TJ?
I told you it was a good piece of content.
TJ's over there being all upset at me for bringing up my favorite thing.
Being like, oh, here's prime bringing up his classic thing.
This is great because I knew Casey would be like, what?
I don't get this.
What the heck's happening here?
I'm right there with you.
There were two ways you could have done this correctly, and they chose neither.
So, like, I would be impressed if someone in the YouTube comments can convince me that this is not like just a bad design,
that this is not just a bug in the way.
the way you've defined the like this is just a good.
Here's why they can't fix it is because it,
it would make JavaScript,
that it would just break everything
that they've already done in JavaScript world.
That's the main reason.
That's a good answer, by the way.
It had to make TypeScriptically adoptable for JavaScript
and that's, I think that's why.
I don't see why.
I don't see that explanation being valid here.
It doesn't look so, so in this case,
the main problem is that there's no way to say
whether something is going, there's no mutability access in the type system for TypeScript, right?
So there's no way to say, like, well, there is, but like not when you're writing it this way.
There's no way to tell you, actually, I'm going to mutate the array that I'm passing.
So like in a normal case, what you're saying is if this function takes numbers or string arrays,
that's fine.
It does successfully handle that case.
That function does not break any promises of its function,
signature or its return type. The problem is there's a separate access of it has mutated that
array to now basically upgrade or like mutate the type itself. It now took something that was
possibly a number array or a string array and turned it into either a number array or number
or string array. But that's not tracked in the type system at all. I don't know why trash.
I don't know. Don't worry. I've switched the grid so we'll leave that as the full thing.
But this is good. This is great.
but like I went through the different cases and I had I and trash typed them in. We saw literally
that the compiler can go oh if if I have something that's declared as only a string as only an
array of string and I try to push one it will error right. Now what that says to me is that it
actually was it does it has the logic for looking at push and saying if it's a string I can't do it.
So when you called when someone called that function and you said
you pass a string, what it should have done is do the type checking on that function at that time.
At the dispatch point, right? This is how template compiling works in C. At the dispatch point to go,
could I actually have this function evaluate with this type? And the answer is clearly no. Even if they run the code,
they already have in the compiler, because we saw it error on the push code. So this is a bug in the compiler.
It should have done that type checking, and it should have done, like, again, this is my first time seeing typeship, so maybe I'm missing something.
But, like, no, from what I saw, this is a bug.
This is a bug that should have been fixed, because you should have done at the dispatch point, and you should have gone, I'm instantiating one of string.
I'm effectively instantiating your version of this function call that would be used for string.
And I don't have to actually instantiate a new version of the function.
I just have to do the type checking for that function, and it didn't do that.
It should have done that.
So, so fight me, YouTube.
I would love nothing more than this bug getting fixed and never have to hear Prime talk about this on another podcast or stream or video again.
So you're preaching to the choir big time.
If there's a way that this can get fixed, I will be so happy.
I will stand TypeScript for months.
I will say it's a great programming language.
I will say.
TJ's positivity art already in the background.
It's already gone.
Let me just illustrate, like, just to be clear, like what I'm talking about.
So I'm trying to make a mental model of the compiler in my head, right?
So when I define a function, right, I'm going to have a function, and 99% of the time that function is going to have an explicit type, let's say.
So it's going to say like I take, like he had before, I take person, I take type person, right?
Yep.
And so in that case, you can just type check the function at the time when you're actually just looking at the function.
In other words, there's only one type check that only has to happen for this, and that is the type check for the type that said it took.
but as soon as you have an or in there,
then what the compiler should have done
is it should have said,
okay, I don't know how this will be called yet, right?
Because now one option is we could type-check all permutations,
string, number, and string-ore number.
But the problem is, as you add more of those,
you could get into a thing where it's like,
okay, we're in factorial land,
and then your compile time blows up, so you wouldn't do that.
All you would do is say, okay, I'm going to defer type-checking of this function.
I'm going to defer it until it's called, right?
That's what you would do.
You can still run a type check on it if you just want to run a type check to see if it ever could do it, right?
Meaning if you could do the inclusive type check of all possible things if you want to.
You could do that if you want to, I don't care.
But point being we should put it to the side and say, okay, here's the, like, effectively like it's a template.
Here's all the things that could have been called as we're just going to remember those, right?
Then as it goes through call points, what the Kappara should do is look at the call point.
it should go, okay, do I have a cached type check for this type signature that I'm about to dispatch, right?
If the answer is yes, that I don't need to do anything, because I've already outputed the errors or not any errors if it was correct.
If I don't have a cached version of that type combination that are passing in, I need to do the type check now.
I do that type check, and if it's an error, I put the error on the dispatch point, right?
and I say, invocation of this would have created an error at this line in the function,
meaning the push would fail because it's pushing an integer and I only had a string array, right?
That is what the compiler should have done.
You would then put that in the cache so you'd never do it again, so it's not a performance penalty to do this, right?
But that's how you would do it.
And so if they're not doing that, I would argue that's a bug.
And someone would have to explain to me why that compilation process I just described
somehow create some unworkable situation that simply won't allow us to create JavaScript anymore.
Like you were saying, like you were saying, like, if there's, I'm totally willing to leave there's something I don't know that's that.
But of the things I've been shown, there's no reason that compilation model wouldn't work.
So I would consider it a bug until someone explains to me what the secret thing is that makes this not possible.
Does that make sense?
Well, the code still runs.
It's that types script now lies about its types.
Right, that would be the thing.
So I'm okay with saying it's a bug in the sense like the TypeScript compiler, right?
Yeah.
It needs to either update its type or say, hey, this type is actually like becomes expanded afterwards.
No, it doesn't need to do that at all.
It just needs to output the error.
Yeah, it would be fine in the case where we're explicitly typing it with saying string, like array at the beginning.
But like I'm saying, TypeScript's design is not like,
has to have been from the start,
and maybe, like, they could really
TypeScript 6.0 and change their mind about it,
because now it's a very different situation,
was, like, code that runs in JavaScript,
like, they wanted to still kind of be able to run
in TypeScript land, right?
Like, you can still in array.
But it will.
Everything I just said, like, literally nothing happens
to the typescrap compiler at all,
except that if you pass something
that wasn't a type that could make this function compile normally,
then it will produce
the error, right? So it's literally just asking for that error to be produced when you use a
dispatch that would have been illegal. It's not asking to mutate any types. Yeah, but it's not.
I think like dispatches problem, I don't know. I think like, I don't know, maybe like in this case,
they could solve this one, but I don't think, uh, like exhaustively they can, they can solve this,
like, problem because sometimes this works. Like it's, if they didn't, if they didn't, if they didn't
They would have to then, okay, sorry.
There's nothing in the type, like, for the function.
There's nothing in the type for the function that says it mutates this.
So if you're like, it needs to get expanded out like a template,
and then they could check everything if it works.
That would, like, Trash is saying,
TypeScript compiler doesn't scale for them.
Like, I promise you, it is doing a lot of things to avoid getting to, like,
anywhere close to the situation where you're talking about right now.
It is not a tenable option for them to make it look or act like a template.
Like if it says it takes these things in as the type, then like that's what it has.
I don't know if that makes sense or not.
I mean, it kind of does, but I mean, I'm just saying that in the, in the worst case scenario,
then really all you're going to get is a few more.
compiles of this. I mean, okay, so I guess what I would say is it sounds to me like type scripts
already is not providing a some kind of guarantee on compile time. There's not generic.
The thing you're thinking of is you have this like generic picture in your head that doesn't exist.
There's only one function that ever exists. And so it doesn't actually have a string version of
the string number or a number version of the string number. It just. But it's not, I'm not asking it
for it to change the code. I'm just saying it could just run it to type check.
It just has to run the type.
I see what you're saying, Casey.
Like, I get what you're saying, because you're saying, like, if this was,
if we imagined this more like how templates work in the sense that, like, it's stuck typing,
I want to see, does this work for a number array, does this work for a string array,
or does this work for a number or string array?
Like, I get what you're saying there, and you're saying, hey, if you passed in a pure string array,
this should error because we're now going to push a number into it.
And now you're lying about the type.
Yeah, the type expanded effectively internally.
Yes. Well, I'm not I'm not really saying that explicitly. All I'm trying to say is the existing compiler that they have already does this error checking. So another way to say this would be, let's suppose that what you were saying was a legitimate defense of this behavior. If that was the case, then the dot push one that trash put in should have worked. Because your argument here, right, is that, well,
I don't want to handle expanding types.
I just want to go like, okay, if it was in JavaScript, it should work.
So the push one should have worked everywhere.
But the fact that the push one didn't work on something that it knew was a string is misleading to the programmer,
because now it's telling the programmer that what this thing does is it type checks the type you have
against the functions that you're calling on that type.
And then when a programmer goes and passes a string type to this function, they very clearly would
expect that function to run the type check, which would have dot push one fail. Because there isn't
any additional analysis that it needs to do. It's the exact same analysis it was already doing
on the push one before. So what I'm saying is literally, they don't even have to do any new work.
They don't have to check any different types, nothing. All they have to do is when they have
their ore operator for types, they just have to remember, okay, I should store the fact that I haven't
type-checked this function completely yet.
That's it. So I'll do one type-check
on it, which is the one I'm already doing.
The compiler stays the same currently, but
I record a little thing into my table
because, you know, it's got a table of functions
and their types. That's the only way it can do any
of the rest of the type-check it's about to do. So I have a table
of function of their types. Every time I see
someone call one, what I should do
is rerun the type-checker. Now, if
you're worried about compile time, you could have a flag
to turn this off. If you're like, don't check
my dispatches, right? You could do that
if that's what you want. But presumably
the only reason people are adding all of the compiled time overhead of typescripted the first place
is because they wanted the type checking.
So what you should at least do is have a thing that every time you have a dispatch, you go and look,
have I type checked this version of the dispatch?
And if you haven't, you just call the type checker.
It will literally just work.
Like, that's all you need to do is just have a little cache of what type combinations
have you type checked.
That's it.
There's no nor code generation, nothing.
Like, it's the exact same compiler otherwise.
It just calls a type checker on more permutations.
That's it.
No, I understand what you're saying.
But unfortunately, we are actually out of time for this episode.
We got, we have to shut this down.
This was a long one.
Thanks, trash.
I'll come back, Casey, with a research.
I was enjoying their banter.
I was just admiring the discussion.
I'm going hard on this one, because I want an actual explanation from somebody who could tell me why we can't do this.
Like, let's go do this in the compiler tomorrow.
that's the stream we add this type second oh i love that there we go right i would love to get it fixed
that would make me happy this is doable people this is doable this is doable this is doable out there
let's do it yeah i mean i i love this we can't add it anymore i love you kasy love you trash
love you tj well like you tj you always insulting everybody over here uh insulting me on my
i said was happy things today what though hey trash appreciate the talk thanks for taking the time
Thanks, trash
That was awesome
By the way, this is actually
Pretty fun
We should do this again
Because the idea of having a presentation
Where we can stop and interrupt
It's actually pretty cool
I really appreciate that you're all stopped
And we started having discussions
Because I've been really awkward
If I was just talking
Yeah
Because I can't see anything
I'm like was that good?
Was that funny?
Did you like it?
I don't know
Let's go fix the typescript compiler
Let's not fix the type script compiler
It will be a lot harder
than you realize it will be
Yeah, I'm sure.
I don't necessarily want to look at the code, but
But we can do this.
This is doable.
This is doable.
I believe in you, Casey.
Yeah, Casey, you got this, Casey.
All you.
All right.
I'll go download it.
That was fun.
Love the presentation, Trash.
Thank you.
Thank you.
It was fun.
It was fun.
I enjoyed it.
All right.
I am rating out right now.
I'm going to, why do we have to end?
Because I have a meeting that I have to go to now.
So,
bye-bye, everybody.
Bye, bye, bye, bye, bye, bye, bye, bye, bye.
Thanks, thanks for Ashley, everybody.
Put up the day,
vibecoating errors on my screen.
Terminal coffee and hair.
