Enterprise TypeScript

DefinitelyTyped & Versioning

Mike North

Mike North

Stripe
Enterprise TypeScript

Check out a free preview of the full Enterprise TypeScript course

The "DefinitelyTyped & Versioning" Lesson is part of the full, Enterprise TypeScript course featured in this preview video. Here's what you'd learn in this lesson:

Mike explains that declaration files, denoted by the .d.ts extension, contain type information but no runnable code. The community-run DefinitelyTyped repository is toured, and students are encouraged to contribute to projects that need type information or bug fixes. Challenges with versioning are also discussed.

Preview
Close

Transcript from the "DefinitelyTyped & Versioning" Lesson

[00:00:00]
>> All right, now, a little written content here. So let's talk about declaration files. These are those .d.ts files. They contain no runnable code, no JavaScript. I'm just gonna pull one up so that we can look at it, it'll be the one that we built together, chat-stdlib, dist.

[00:00:25]
We'll go with this beta one, and I'll get rid of that and this. So this is interesting, right? We're saying, there's this function thing. There's a function, but there's no function body. In fact, I'd be yelled at, I'm not allowed to have something in here, like console.log, cannot do it.

[00:00:47]
This is an implementation. Implementations are not allowed in ambient contexts. So we would call declaration files ambient types, it just means it's just the type information. Now, why this distinction? Why are we saying these are not exactly the same thing or not always the same thing? You could go into this src file, this ts file, and you could do something like this.

[00:01:27]
No, this is easier. Just making something interesting that proves, did I have to export here? Yep, okay, and now I could do window.my, It's not gonna do that for me. Sorry, I have to do it a different way. So we're taking advantage of open interfaces here, and we're saying, I wanna add something to window called mySpecialProperty.

[00:02:15]
So we've used an open interface on window here, and look, I can hover over this, and mySpecialProperty is in here. This here we would also describe as an ambient context. So eslint is objecting before Typescript even gets a chance to start working on these things. But these are also ambient types, they're also declaration space.

[00:02:50]
I'll call this implementation space, where you can actually have assignments of variables, give them values, they're not holding a value. Now, part of what's confusing about this is, I'll leave this in common to that just so you can find it later. Part of what's confusing is you can end up with things in here that kind of look value-ish.

[00:03:17]
You could do this. Something like that. Well, how about this? Am I allowed to do that? Okay, I can't do that either. Interesting, I could do it with the namespace, but part of what takes a little bit of getting used to is what you're allowed to do in this space and what you're not.

[00:04:06]
So you can have imports, you can have exports. When you're declaring a class here, it looks like you're declaring a class similar to if we were to look at the source code here, right? We've still got class Deferred and we've got all of these fields, and they look very similar.

[00:04:23]
But you can have a constructor here, you're just simply defining that I have a class, there's an interface stacked on top of it that represents an instance of the class, it's just the type information. Sorry, I wanna try one more thing. That looks like an assignment, doesn't it?

[00:04:49]
Sorry, this is what I was trying to recall earlier. It's not, though, it's just saying foo is of type 6, it's literally the same thing as this. You can just do things with literal types that way for some reason. So you can't have statements, only expressions. You can't have anything reassignable.

[00:05:12]
I don't think you can do that. No, you can, was wrong there. And, One of the things you have to be careful of, it's manually authoring these. Sometimes you have no other choice, if you're working with a JavaScript library that has no types associated with it yet, odds are you'll have to write one of these.

[00:05:33]
But it is far better to have one that's sort of generated from TypeScript code, it just ensures that there's parody between the two things. Speaking of manually written declaration files being a tough thing to maintain, let's talk about community written types. So there's a git repo called DefinitelyTyped.

[00:05:54]
Okay, so here's DefinitelyTyped, and let's look at this type's folder. I wonder whether GitHub can handle this now. Sorry, it truncated it to 1000 files, which it means folders in this context, and it had to drop 7,723 folders. And if we look down here, let's see if we can find something interesting.

[00:06:17]
Alt, here's angular, anchor-js. There's a lot of different types here. Await-promises, autoprefixer, babel-core. Just looking for something that's gonna be a little bit more familiar to everybody.
>> Is this the repo when you install @types?
>> It is absolutely that repo. So let me go to this one, and then I'm just gonna manually change the URL.

[00:07:00]
We're gonna look at Reacts types. This is gonna be a little bit of, gonna be scary. Not trying to jump scare anyone. So here's your warning. But if we look at these, these are handwritten. And you can see here, here's a namespace for React. Here's this type called ElementType.

[00:07:22]
So here's some utility types that they've built, a RefObject, a LegacyRef, ElementRef, ReactElement, right? There's some JSX-related types in here. These are meant to mirror the things that you get from React.js, a React child that feels like it's probably a member of the children object, a fragment, a React node.

[00:07:52]
Look at all these, you can render a string or ReactElement. This is probably something that you can spit out in JSX, something you could return from an element. createFactory, so now we're getting into functions that you can use in React. createElement, this is the way you bootstrap your React app, but they're just function declarations.

[00:08:17]
And this, if we hit Blame, please don't make me a liar, React. But if this is most definitely typed packages, it's written by tons and tons of different people. So you've got a bunch of community contributors here, individually authoring different lines, making different fixes. They're doing their best to try to keep this lined up with the React library.

[00:08:42]
When you have @types/react, this is what you're getting. You're getting these declaration files, and there's a lot of hard work that goes into these to keep them lined up. So look here, we've got even React v16, v17. V15 is in there. And if we look at their package.json, they might even have types versions.

[00:09:07]
Let's check that out if it's here, maybe not. Owners, there it is. So here, this is a field you can add to a package.json, that says, depending on which TypeScript compiler version you're using, I have a different declaration file for you. I can have a legacy file that works for TypeScript 3, and a very modern one that works for TypeScript 5.

[00:09:35]
And this is a big important part of making sure that this package here is gonna work for a wide range of people, right? You still need to get those bug fixes in. But it's very manual work, where let's see if we look at V17, they might have a bunch of different versions in there.

[00:09:59]
They've structured this one a little bit differently, but yeah, here's the ts5.0 ones. It's common to see in these that they'll sort of import the legacy types as a starting point and then add new stuff on top of it. So there's import React from ./, where's the index?

[00:10:18]
Do we just look at this? PropTypes, yeah, these are in all likelihood importing. It looks like we're consuming it from this library, from prop-types, but this is just types in this file. Where this is really coming from in all likelihood is @types/prop-types, which is another folder in this project, it's this one.

[00:10:50]
So you're importing PropTypes, but it's just the types you're dealing with cuz that's all that can be in these declaration files. So why am I bothering to tell you about these? Well, first off, it's a great project, it's managed by the TypeScript core team. And when I say manage, they built a lot of tooling around it to make sure that things get published right away.

[00:11:17]
And they even have a policy, where if somebody submits a change to the types, the people who are listed as owners, which is gonna be up at the top of their index file. Maybe they have a different, it's in the package.json now. Sorry, it's been a while since I contributed to this.

[00:11:37]
So if I submitted a change to types here, these three people would get mentioned in a GitHub comment on the poll request. And they'd have a few days to come in and either provide feedback saying, I want changes, or this looks good, but it defaults to letting changes go through.

[00:11:57]
It seems a little scary and it's a little bit, but trust that the types that lots and lots of people use have a long list of owners and they know how this works and they'll respond. But it also means that if you find a library, like type definitions that are abandoned, you're not at a dead end.

[00:12:17]
You can add yourself as an owner if you want to take them over, and you can start to help maintain these things. There's another challenge around definitely typed, and it has to do with versioning. So if you look at the versions of the @types packages, you may have noticed this, even today as we were installing a couple of them.

[00:12:38]
Version number of @types package matches version number of the thing, the types describe, which makes it very easy to know which thing to install, right? You want those numbers to line up. We would call that lockstep versioning, right? A new patch release comes out in Facebook, sorry, in React, @type/react releases another version of the types to include that.

[00:13:05]
But the thing is, sometimes the types themselves break, or they introduce breakage, or they drop support for old compiler versions. And my argument here is we end up with more bits of information than you can capture in a semantic versioning number here. You need to truly capture that in order to really communicate risk, right?

[00:13:27]
The types, there are bug fixes that are specific to the types. And every time those are merged, a release is automatically cut, a number ticks up. And so one thing I'd suggest you be careful with, if you hit errors relating to definitely typed packages, just understand that this is how it works.

[00:13:48]
And that sometimes big changes to those @types packages can happen. They'll drop support for an older compiler version. But it'll look just like a patch release or a middle-digit release because they really prioritize lining up with version numbers of the library that they describe over, communicating breaking and non-breaking changes within the @types package.

[00:14:13]
It's a hard problem to solve, you'd need at least a five component number to really communicate. It's like major of library, minor of library, major of types, minor of types, something like that.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now